暑假组队训练——最小生成树

先放板子:

kruskal: 时间复杂度: O(m\log m)

int n, m;       
int p[N];       

struct Edge     
{
    int a, b, w;

    bool operator< (const Edge &W)const
    {
        return w < W.w;
    }
}edges[M];

int find(int x)     
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal()
{
    sort(edges, edges + m);

    for (int i = 1; i <= n; i ++ ) p[i] = i;   

    int res = 0, cnt = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;

        a = find(a), b = find(b);
        if (a != b)     
        {
            p[a] = b;
            res += w;
            cnt ++ ;
        }
    }

    if (cnt < n - 1) return INF;
    return res;
}

prim: 时间复杂度:O(n^2+m)

int n;      
int g[N][N];        
int dist[N];        
bool st[N];    


int prim()
{
    memset(dist, 0x3f, sizeof dist);

    int res = 0;
    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        if (i && dist[t] == INF) return INF;

        if (i) res += dist[t];
        st[t] = true;

        for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
    }

    return res;
}

A,B,D,H,J,K,M

C - Building a Space Station

 题意:给n个点的三维坐标,求这n个点构成的最小生成树

计算出n*(n+1)/2条边,跑kruskal即可。(注意精度)

E - Truck History

题意:给n个字符串,每个字符串中相同位置不同字符的个数代表两条串的距离,求连接所有字符串的最小路径的倒数。

一样是计算出n*(n+1)/2条边,跑kruskal

F - Arctic Network

 题意:有n个点之间需要建立网络,使用两种技术,一种卫星频道,可以无视距离联系,但是个数k有限,一种是无线电,需要两个点距离不超过D才能建立联系,功率越高,D越大,需要确定最小的D值,在图中建立一个生成树。

分析:二分+最小生成树kruskal,对D值二分,建立最小生成树时,在每次加边时额外判断距离是否大于D,最后在判断是否连接了所有点时,在已连接的边数加上k,表示未连接的边使用卫星频道联系,若判断完全连接,即得到最小D值。

代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1100, M = 2e5+10;

int n, m, k;
int p[N];

struct Edge {
	int a, b;
	double w;
	
	bool operator< (const Edge &W)const {
		return w < W.w;
	}
} edges[M];

int find(int x) {
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}

struct Center {
	double x, y;
} pos[N];

double kruskal(double d) {
	sort(edges, edges + m);
	
	for (int i = 1; i <= n; i ++ ) p[i] = i;
	
	double res = 0;
	int cnt = 0;
	for (int i = 0; i < m; i ++ ) {
		int a = edges[i].a, b = edges[i].b;
		double w = edges[i].w;
		
		a = find(a), b = find(b);
		if (a != b && d > w) {
			p[a] = b;
			res += w;
			cnt ++ ;
		}
	}
	//cout<<cnt+k-1<<endl;
	if (cnt + k - 1 < n - 1) return -1;
	return res;
}

inline double mul(Center a, Center b) {
	double res = sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
	if (res > 0) return res;
	else return 0;
}

bool check(double mid) {
	if (kruskal(mid)>0) return true;
	else return false;
}

int main() {
	int T;
	cin >> T;
	while (T--) {
		m = 0;
		cin >> k >> n;
		for (int i = 1; i <= n; i++) cin >> pos[i].x >> pos[i].y;
		for (int i = 1; i <= n; i++) {
			for (int j = i + 1; j <= n; j++) {
				double w = mul(pos[i], pos[j]);
				edges[m] = {i, j, w};
				m++;
			}
		}
		double l = 0, r = 10000;
		while (r - l > 1e-5) {
			double mid = (l + r) / 2;
			if (check(mid)) r = mid;
			else l = mid;
			//cout<<mid<<endl;
		}
		printf("%.2lf\n",l);
		//for(int i=0;i<m;i++) cout<<edges[i].a<<" "<<edges[i].b<<" "<<edges[i].w<<'\n';
	}
	return 0;
}

G - Highways

 题意:给n个点的坐标,在部分点之间已经有边,要求建立n个点的最小生成树。

分析:kruskal最小生成树,读入已建成的边时将其加入最小生成树中,再将未建成的边加入树中

代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10, M = 3e5 + 10;
typedef pair<int, int> PII;

int n, m, k;
int p[N];
map<PII, int> mp;
vector <PII> v;

struct Edge {
	int a, b;
	double w;

	bool operator< (const Edge &W)const {
		return w < W.w;
	}
} edges[M];

struct Center {
	double x, y;
} pos[N];

inline double mul(Center a, Center b) {
	double res = sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
	if (res > 0) return res;
	else return 0;
}

int find(int x) {
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}

double kruskal() {
	sort(edges, edges + m);

	double res = 0;
	int cnt = 0;
	for (int i = 0; i < m; i ++ ) {
		int a = edges[i].a, b = edges[i].b;
		double w = edges[i].w;
		int pa = find(a), pb = find(b);
		if (pa != pb) {
			p[pa] = pb;
			res += w;
			cnt ++ ;
			v.push_back({a, b});
		}
	}

	if (cnt + k < n - 1) return -1;
	return res;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i ++ ) p[i] = i;
	for (int i = 1; i <= n; i++) {
		cin >> pos[i].x >> pos[i].y;
	}
	for (int i = 1; i <= n; i++) {
		for (int j = i + 1; j <= n; j++) {
			double w = mul(pos[i], pos[j]);
			edges[m] = {i, j, w};
			m++;
		}
	}
	int res = 0;
	cin >> k;
	for (int i = 0; i < k; i++) {
		int a, b;
		cin >> a >> b;
		int pa = find(a), pb = find(b);
		if (pa != pb) {
			p[pa] = pb;
			res++;
		}
	}
	k = res;
	kruskal();
	for (int i = 0; i < v.size(); i++) {
		cout << v[i].first << " " << v[i].second << endl;
	}
	return 0;
}

 I - The Unique MST

题意:给定一个连接的无向图,告诉它的最小生成树是否是唯一的。

分析:判断最小生成树的唯一性,即如果存在其他最小生成树,说明至少有两条边存在相同权值且连接的点至少有一个相同,在kruskal建立最小生成树时,标记已经使用的边,并记录连接两点的边的权值,建完树后,遍历一遍没有被选用的边,若找到这样的边,说明非唯一。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10, M = 1e5 + 10;
typedef pair<int, int> PII;

int n, m;
int p[N];
int mp[N];

struct Edge {
	int a, b, w;
	bool operator< (const Edge &W)const {
		return w < W.w;
	}
} edges[M];

int find(int x) {
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}

int fg[N];

int kruskal() {
	sort(edges, edges + m);

	for (int i = 1; i <= n; i ++ ) p[i] = i;

	int res = 0, cnt = 0;
	for (int i = 0; i < m; i ++ ) {
		int a = edges[i].a, b = edges[i].b, w = edges[i].w;

		int pa = find(a), pb = find(b);
		if (pa != pb) {
			p[pa] = pb;
			res += w;
			cnt ++ ;
			fg[i] = 1;
			mp[a]=mp[b]=w;
		}
	}

	if (cnt < n - 1) return -1;
	return res;
}

int main() {
	int T;
	cin >> T;
	while (T--) {
		memset(fg,0,sizeof(fg));
		int flag = 1;
		cin >> n >> m;
		for (int i = 0; i < m; i++) {
			cin >> edges[i].a >> edges[i].b >> edges[i].w;
		}
		int ans = kruskal();
		for (int i = 0; i < m; i++) {
			if (!fg[i]) {
				int a = edges[i].a, b = edges[i].b, w = edges[i].w;
				if (mp[a] == w || mp[b] == w) {
					flag = 0;
				}
			}
		}
		if (flag) cout << ans << endl;
		else cout << "Not Unique!\n";
	}
	return 0;
}

L - 畅通工程再续

题意:给定n个点坐标,求最小生成树,对每条边长度要求为大于10,小于1000

分析:在kruskal中额外加一个判断长度的条件即可

N - Qin Shi Huang's National Road System

 题意:秦始皇要在n个城市之间建立n-1条路,使它们连接起来,他希望所有道路总长度最小,徐福可以修一条不需要花钱的魔道,秦始皇希望所有非魔法道路总长度越小越好,徐福希望魔法道路让尽可能多的人受益,所以他决定A/B的值必须最大,其中A是魔法道路连接的两个城市的总人口,B是非魔法道路的总长度。

分析:

代码:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值