[kuangbin带你飞]专题六 最小生成树 题解


A - Jungle Roads (POJ - 1251)

思路: 把字母转化为数字,然后再跑一遍最小生成树。

AC Code:

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn = 30;
struct Edge{
	int u, v, w;
	bool operator < (const Edge &r) const {
		return  w < r.w;
	}
}E[maxn * maxn];
int n, m, ans, f[maxn];
char ch1, ch2;
int find(int x) {
	return x == f[x] ? x : f[x] = find(f[x]);
}
void Kruskal(int n, int m) {
	int num = 0, result = 0;
	for(int i = 1; i <= n; i++)
		f[i] = i;
	sort(E + 1, E + 1 + m);	
	for(int i = 1; i <= m; i++) {
		int f1 = find(E[i].u);
		int f2 = find(E[i].v);
		if(f1 != f2) {
			num++;
			f[f1] = f2;
			result += E[i].w;
		}
		if(num == n - 1)
			break;
	}
	printf("%d\n", result);
}
int main() {
	while(~scanf("%d", &n)) {
		if(n == 0)
			break;
		m = 0;
		for(int i = 1, w; i < n; i++) {
			scanf(" %c %d", &ch1, &ans);
			for(int j = 1; j <= ans; j++) {
				scanf(" %c %d", &ch2, &w);
				E[++m].u = ch1 - 'A' + 1;
				E[m].v = ch2 - 'A' + 1;
				E[m].w = w;
			}
		}
		Kruskal(n, m);
	}
	system("pause");
	return 0;
}

B - Networking (POJ - 1287)

思路: 最小生成树模板题

AC Code:

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn = 110;
struct Edge{
	int u, v, w;
	bool operator < (const Edge &r) const {
		return w < r.w;
	}
}E[maxn * maxn];
int n, m, f[maxn];
int find(int x) {
	return x == f[x] ? x : f[x] = find(f[x]);
}
void Kruskal(int n, int m) {
	int num = 0;
	int result = 0;
	for(int i = 1; i <= n; i++)
		f[i] = i;
	sort(E + 1, E + 1 + m);
	for(int i = 1; i <= m; i++) {
		int f1 = find(E[i].u);
		int f2 = find(E[i].v);
		if(f1 != f2) {
			f[f1] = f2;
			result += E[i].w;
			num++;
		}
		if(num == n - 1)
			break;
	}
	printf("%d\n", result);
}
int main() {
	while(~scanf("%d", &n)) {
		if(n == 0)
			break;
		scanf("%d", &m);
		for(int i = 1; i <= m; i++) {
			scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
		}
		Kruskal(n, m);
	}
	system("pause");
	return 0;
}

C - Building a Space Station (POJ - 2031)

思路: 求出每个球心直接的距离,如果两球心间的距离小于两球的半径和,那么就令这条边的长度为0,否则为两球心间的距离减去两球半径和。

AC Code:

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn = 110;
struct Edge{
	int u, v;
	double w;
	bool operator < (const Edge &r) const{
		return w < r.w;
	}
}E[maxn * maxn];
int n, m, t, f[maxn];
struct Point{
	double x, y, z, r;
}P[maxn];
double Dist(Point a, Point b) {
	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z));
};
int find(int x) {
	return x == f[x]?x:f[x] = find(f[x]);
}
void Kruskal(int n, int m) {
	int num = 0;
	double result = 0;
	sort(E + 1, E + 1 + m);
	for(int i = 1; i <= n; i++)
		f[i] = i;
	for(int i = 1; i <= m; i++) {
		int f1 = find(E[i].u);
		int f2 = find(E[i].v);
		if(f1 != f2) {
			result += E[i].w;
			num++;
			f[f1] = f2;
		}
		if(num == n - 1)
			break;
	}
	printf("%.3f\n", result);
}
int main() {
	while(~scanf("%d", &n)) {
		if(n == 0)
			break;
		for(int i = 1; i <= n; i++) {
			scanf("%lf %lf %lf %lf", &P[i].x, &P[i].y, &P[i].z, &P[i].r);
		}
		int ans = 0;
		for(int i = 1; i < n; i++) {
			for(int j = i + 1; j <= n; j++) {
				E[++ans].u = i;
				E[ans].v = j;
				if(Dist(P[i], P[j]) < P[i].r + P[j].r)
					E[ans].w = 0;
				else
					E[ans].w = Dist(P[i], P[j]) - P[i].r - P[j].r;
			}
		}
		Kruskal(n, ans);
	}
	system("pause");
	return 0;
}

D - Constructing Roads (POJ - 2421)

思路: 建边和之前有一点不一样,MST模板题

AC Code:

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn = 110;
struct Edge{
	int u, v, w;
	bool operator < (const Edge &r) const {
		return w < r.w;
	}
}E[maxn * maxn];
int n, m, f[maxn];
int find(int x) {
	return x == f[x] ? x : f[x] = find(f[x]);
}
void Kruskal(int n, int m) {
	int num = 0;
	int result = 0;
	for(int i = 1; i <= n; i++)
		f[i] = i;
	sort(E + 1, E + 1 + m);
	for(int i = 1; i <= m; i++) {
		int f1 = find(E[i].u);
		int f2 = find(E[i].v);
		if(f1 != f2) {
			f[f1] = f2;
			result += E[i].w;
			num++;
		}
		if(num == n - 1)
			break;
	}
	printf("%d\n", result);
}
int main() {
	scanf("%d", &n);
	int ans = 0;
	for(int i = 1, x; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			scanf("%d", &x);
			if(i > j) {
				E[++ans].u = i;
				E[ans].v = j;
				E[ans].w = x;
			}
		}
	}
	scanf("%d", &m);
	for(int i = 1; i <= m; i++) {
		scanf("%d %d", &E[++ans].u, &E[ans].v);
		E[ans].w = 0;
	}
	Kruskal(n, ans);
	system("pause");
	return 0;
}

E - Truck History (POJ - 1789)

题意: n个卡车类型,刚开始只有一个类型,其他类型都是由一个类型直接或间接产生,一个类型到另一个类型的花费是它们之间的字符不同的个数,问产生所有类型卡车的最小花费。

思路: 看懂了题意这题就很简单了,就是一道MST的水题。

AC Code:

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn = 2e3 + 10;
struct Edge{
	int u, v, w;
	bool operator < (const Edge &r) const {
		return w < r.w;
	}
}E[maxn * maxn];
string st[maxn];
int n, m, f[maxn];
int find(int x) {
	return x == f[x] ? x : f[x] = find(f[x]);
}
void Kruskal(int n, int m) {
	int num = 0;
	int result = 0;
	for(int i = 1; i <= n; i++)
		f[i] = i;
	sort(E + 1, E + 1 + m);
	for(int i = 1; i <= m; i++) {
		int f1 = find(E[i].u);
		int f2 = find(E[i].v);
		if(f1 != f2) {
			f[f1] = f2;
			result += E[i].w;
			num++;
		}
		if(num == n - 1)
			break;
	}
	cout<<"The highest possible quality is 1/"<<result<<".\n";
}
int main() {
	while(cin>>n) {
		if(n == 0)
			break;
		for(int i = 1; i <= n; i++)
			cin>>st[i];
		int ans = 0;
		for(int i = 1; i < n; i++) {
			for(int j = i + 1; j <= n; j++) {
				int k = 0;
				for(int p = 0; p < 7; p++) {
					if(st[i][p] != st[j][p])
						k++;
				}
				E[++ans].u = i;
				E[ans].v = j;
				E[ans]. w = k;
			}
		}
		Kruskal(n, ans);
	}
	system("pause");
	return 0;
}

F - Arctic Network (POJ - 2349)

题意: 有s颗卫星和p个哨所,有卫星的两个哨所之间可以任意通信;否则,一个哨所只能和距离它小于等于D的哨所通信。给出卫星的数量和p个哨所的坐标,求D的最小值。

思路: s颗卫星则说明有 s − 1 s - 1 s1条边直接相连,贪心地去想,肯定要让有卫星的哨所直接距离最长,所以只用构造一个 n − 1 − ( s − 1 ) n - 1 - (s - 1) n1(s1)条边的MST了。

AC Code:

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn = 5e2 + 10;
struct Edge{
	int u, v;
	double w;
	bool operator < (const Edge &r) const {
		return w < r.w;
	}
}E[maxn * maxn];
string st[maxn];
int n, m, f[maxn], t, s, p;
struct Point{
	double x, y;
}P[maxn];
double Dist(Point a, Point b) {
	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
int find(int x) {
	return x == f[x] ? x : f[x] = find(f[x]);
}
void Kruskal(int n, int m) {
	int num = 0;
	double result = 0;
	for(int i = 1; i <= n; i++)
		f[i] = i;
	sort(E + 1, E + 1 + m);
	for(int i = 1; i <= m; i++) {
		int f1 = find(E[i].u);
		int f2 = find(E[i].v);
		if(f1 != f2) {
			f[f1] = f2;
			result = E[i].w;
			num++;
		}
		if(num == n - 1 - (s - 1))
			break;
	}
	printf("%.2f\n", result);
}
int main() {
	scanf("%d", &t);
	while(t--) {
		scanf("%d %d", &s, &p);
		for(int i = 1; i <= p; i++) {
			scanf("%lf %lf", &P[i].x, &P[i].y);
		}
		int ans = 0;
		for(int i = 1; i < p; i++) {
			for(int j = i + 1; j <= p; j++) {
				E[++ans].u = i;
				E[ans].v = j;
				E[ans].w = Dist(P[i], P[j]);
			}
		}
		Kruskal(p,  ans);
	}
	system("pause");
	return 0;
}

G - Highways (POJ - 1751)

思路: MST裸题,加个路径输出就好了。

AC Code:

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn = 1e3 + 10;
struct Edge {
	int u, v;
	double w;
	bool operator < (const Edge &r) const {
		return w < r.w;
	}
}E[maxn * maxn];
struct Point{
	double x, y;
}P[maxn];
int n, m, pa[maxn], pb[maxn], f[maxn];
double Dist(Point a, Point b) {
	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
int find(int x) {
	return x == f[x] ? x : f[x] = find(f[x]);
}
void Kruskal(int n, int m) {
	int num = 0;
	int cnt = 0;
	sort(E + 1, E + 1 + m);
	for(int i = 1; i <= n; i++)
		f[i] = i;
	for(int i = 1; i <= m; i++) {
		int f1 = find(E[i].u);
		int f2 = find(E[i].v);
		if(f1 != f2) {
			f[f1] = f2;
			num++;
			if(E[i].w != 0) {
				pa[++cnt] = E[i].u;
				pb[cnt] = E[i].v;
			}
		}
		if(num == n - 1)
			break;
	}
	for(int i = 1; i <= cnt; i++) {
		printf("%d %d\n", pa[i], pb[i]);
	}
}
int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		scanf("%lf %lf", &P[i].x, &P[i].y);
	}
	int ans = 0;
	for(int i = 1; i < n; i++) {
		for(int j = i + 1; j <= n; j++) {
			E[++ans].u = i;
			E[ans].v = j;
			E[ans].w = Dist(P[i], P[j]);
		}
	}
	scanf("%d", &m);
	for(int i = 1, st, ed; i <= m; i++) {
		scanf("%d %d", &st, &ed);
		E[++ans].u = st;
		E[ans].v = ed;
		E[ans].w = 0;
	}
	Kruskal(n, ans);
	// system("pause");
	return 0;
}

H - Agri-Net (POJ - 1258)

思路: MST裸题

AC Code:

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn = 5e2 + 10;
struct Edge{
	int u, v, w;
	bool operator < (const Edge &r) const {
		return w < r.w;
	}
}E[maxn * maxn];
string st[maxn];
int n, m, f[maxn];
int find(int x) {
	return x == f[x] ? x : f[x] = find(f[x]);
}
void Kruskal(int n, int m) {
	int num = 0;
	int result = 0;
	for(int i = 1; i <= n; i++)
		f[i] = i;
	sort(E + 1, E + 1 + m);
	for(int i = 1; i <= m; i++) {
		int f1 = find(E[i].u);
		int f2 = find(E[i].v);
		if(f1 != f2) {
			f[f1] = f2;
			result += E[i].w;
			num++;
		}
		if(num == n - 1)
			break;
	}
	printf("%d\n", result);
}
int main() {
	while(~scanf("%d", &n)) {
		m = 0;
		for(int i = 1, x; i <= n; i++) {
			for(int j = 1; j <= n; j++) {
				scanf("%d", &x);
				if(j > i) {
					E[++m].u = i;
					E[m].v = j;
					E[m].w = x;
				}
			}
		}
		Kruskal(n, m);
	}
	system("pause");
	return 0;
}

I - Borg Maze (POJ - 3026)

题意: 问从S出发,派机器人到A的地方,机器人可以在图中分裂,求最少步数。

思路: 因为机器人可以分裂,那到一个A的最近的距离就是离它最近的A或S。我们可以用BFS搜索任意两个A或S间的距离,然后再求一个最小生成树就是答案了。我认为是道挺好的题目,就是后台的数据有点问题,n和m后面会有空格,需要处理一下。

AC Code:

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn = 60;
char mp[maxn][maxn];
int t, n, m, id[maxn][maxn], vis[maxn][maxn], ans, f[maxn];
int moven[5][5] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
struct Edge{
	int u, v, w;
	bool operator < (const Edge &r) const {
		return w < r.w;
	}
}E[maxn * maxn * maxn * maxn];
struct Node{
	int x, y, id, d;
};
int find(int x) {
	return x == f[x] ? x : f[x] = find(f[x]);
}
bool check(int x, int y) {
	if(x < 1 || x > n || y < 1 || y > n || id[x][y] == -1 || vis[x][y]) return false;
	return true; 
}
void BFS(int x, int y) {
	vis[x][y] = 1;
	queue<Node>q;
	Node tmp;
	tmp.x = x, tmp.y = y, tmp.id = id[x][y], tmp.d = 0;
	q.push(tmp);
	int id1 = id[x][y];
	while(!q.empty()) {
		Node now = q.front();
		q.pop();
		for(int i = 0; i < 4; i++) {
			int mx = now.x + moven[i][0];
			int my = now.y + moven[i][1];
			if(check(mx, my)) {
				vis[mx][my] = 1;
				tmp.x = mx, tmp.y = my, tmp.id = id[mx][my], tmp.d = now.d + 1;
				q.push(tmp);
				if(tmp.id != 0) {
					E[++ans].u = id1;
					E[ans].v = tmp.id;
					E[ans].w = tmp.d;
				}
			} 
		}
	}
}
void Kruskal(int n, int m) {
	int num = 0;
	int result = 0;
	sort(E + 1, E + 1 + m);
	for(int i = 1; i <= n; i++)
		f[i] = i;
	for(int i = 1; i <= m; i++) {
		int f1 = find(E[i].u);
		int f2 = find(E[i].v);
		if(f1 != f2) {
			num++;
			result += E[i].w;
			f[f1] = f2;
		}
		if(num == n - 1)
			break;
	}
	printf("%d\n", result);
}
int main() {
	scanf("%d", &t);
	while(t--) {
		scanf("%d %d\n", &m, &n);
		for(int i = 1; i <= n; i++) {
			gets(mp[i] + 1);
		}
		memset(id, 0, sizeof(id));
		ans = 0;
		int num = 0;
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= m; j++) {
				if(mp[i][j] == 'A' || mp[i][j] == 'S') {
					id[i][j] = ++num;
				}
				if(mp[i][j] == '#')
					id[i][j] = -1;
			}
		}
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= m; j++) {
				if(mp[i][j] == 'A' || mp[i][j] == 'S'){
					memset(vis, 0, sizeof(vis));
					BFS(i, j);
				}
			}
		}
		Kruskal(num, ans);
	}
	system("pause");
	return 0;
}

J - The Unique MST (POJ - 1679)

题意: 问最小生成树是不是唯一的,如果是则输出边权和,如果不是输出Not Unique!

思路: 首先生成一个最小生成树,然后每次删掉其中的一条边,再去生成最小生成树,如果能生成权值和与之前一样的,那说明最小生成树不唯一,否则唯一。

AC Code:

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn = 110;
int t, n, m, ans, f[maxn], path[maxn];
struct Edge{
	int u, v, w;
	bool operator < (const Edge &r) const {
		return w < r.w;
	}
}E[maxn * maxn];
int find(int x) {
	return x == f[x] ? x : f[x] = find(f[x]);
}
void init() {
	for(int i = 1; i <= n; i++)
		f[i] = i;
}
void Kruskal(int n, int m) {
	int num = 0;
	int result = 0;
	sort(E + 1, E + 1 + m);
	init();
	for(int i = 1; i <= m; i++) {
		int f1 = find(E[i].u);
		int f2 = find(E[i].v);
		if(f1 != f2) {
			path[++num] = i;
			result += E[i].w;
			f[f1] = f2;
		}
		if(num == n - 1)
			break;
	}
	int flag = 0;
	for(int i = 1; i <= num; i++) {
		int sum = 0;
		int ans = 0;
		init();
		for(int j = 1; j <= m; j++) {
			if(j == path[i])
				continue;
			int f1 = find(E[j].u);
			int f2 = find(E[j].v);
			if(f1 != f2) {
				ans++;
				sum += E[j].w;
				f[f1] = f2;
			}
			if(ans == n - 1)
				break;
		}
		if(ans == n - 1 && sum == result){
			flag = 1;
			break;
		}
	}
	if(flag)
		printf("Not Unique!\n");
	else
		printf("%d\n", result);
}
int main() {
	scanf("%d", &t);
	while(t--) {
		scanf("%d %d", &n, &m);
		for(int i = 1; i <= m; i++) {
			scanf("%d %d %d", &E[i].u, &E[i].v, &E[i].w);
		}
		Kruskal(n, m);
	}
	system("pause");
	return 0;
}

K - 还是畅通工程 (HDU - 1233)

思路: MST裸题

AC Code:

#include<cstdio>
#include<vector>
#include<string>
#include<algorithm>
#include<iostream>
#include<cstring>
#define N 10005
#define inf 0x3f3f3f3f
using namespace std;
int n,m,f[N];
struct edge{
	int u,v,w;
}e[N];
bool cmp(edge a,edge b){
	return a.w<b.w;
}
int find(int x){
	return x==f[x]?x:f[x]=find(f[x]);
}
int K(int n,int m){
	int c=n,ans=0;
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	sort(e,e+m,cmp);
	for(int i=0;i<m;i++){
		int f1=find(e[i].u);
		int f2=find(e[i].v);
		if(f1!=f2){
			ans=ans+e[i].w;
			c--;
			f[f1]=f2;
		}
		if(c==1)  break;
	}
	if(c>1)  return -1;
	else  return ans;
}
int main(void){
    while(scanf("%d",&n)!=EOF){
    if(n==0)  break;
    m=n*(n-1)/2;
    for(int i=0;i<m;i++){
    	scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
	}
	printf("%d\n",K(n,m));
	}
}

L - 畅通工程再续 (HDU - 1875)

思路: MST裸题,多了个边权的约束。

AC Code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 110;
struct Edge{
	int u, v;
	double w;
	bool operator < (const Edge &r) const{
		return w < r.w;
	}
}E[maxn * maxn];
int n, m, t, f[maxn];
struct Point{
	double x, y;
}P[maxn];
double Dist(Point a, Point b) {
	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
};
int find(int x) {
	return x == f[x]?x:f[x] = find(f[x]);
}
void Kruskal(int n, int m) {
	int num = 0;
	double result = 0;
	sort(E + 1, E + 1 + m);
	for(int i = 1; i <= n; i++)
		f[i] = i;
	for(int i = 1; i <= m; i++) {
		int f1 = find(E[i].u);
		int f2 = find(E[i].v);
		if(f1 != f2 && E[i].w >= 10 && E[i].w <= 1000) {
			result += E[i].w;
			num++;
			f[f1] = f2;
		}
		if(num == n - 1)
			break;
	}
	if(num == n - 1)
		printf("%.1f\n", result * 100);
	else
		puts("oh!");
}
int main() {
	scanf("%d", &t);
	while(t--){
		scanf("%d", &n);
		for(int i = 1; i <= n; i++) {
			scanf("%lf %lf", &P[i].x, &P[i].y);
		}
		int ans = 0;
		for(int i = 1; i < n; i++) {
			for(int j = i + 1; j <= n; j++) {
				E[++ans].u = i;
				E[ans].v = j;
				E[ans].w = Dist(P[i], P[j]);
			}
		}
		Kruskal(n, ans);
	}
	system("pause");
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值