UVA - 10034 Freckles (kruskal算法)

题目大意:

在"Dick Van Dyke"的某一集中,小 Richie 尝试把他爸爸背上的斑点连成独立钟的图案。但是其中一个斑点是伤疤,所以 Richie 的尝试失败了。

把 Dick 的背看成一个平面,上面有不同位置的斑点 (x,y)。你的任务是要告诉 Richie 应该怎样连接所有点,使得所用的墨水最少。Richie 总是用直线段连接两个点,在画不同线段之间可以将笔提起。当Richie 结束画线时,任何一个斑点必须能够经过所画的线到达任何另外一个斑点。  

[输入]  
输入第一行只有一个正整数,代表测试数据的数量。接下来有一个空行。  
每组数据的第一行有一个整数 n(0 < n <= 100),表示 Dick 背上的斑点数。接下来的每行有两个实数 (x,y),分别代表他背上每个斑点的位置。相邻两组输入数据之间有一个空行。  

[输出]  

对于每组数据,你的程序要输出一个保留两位小数的实数,表示将所有斑点连接起来所需线段的最小总长度。相邻两组数据的输出之间应该有一个空行。

解析:

最小生成树的问题,先将点和点间的距离转换为包含起始点和终点、以及权重的边,然后套kruskal的模板。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 110;
const int MAX = 5050;

struct Edge {
	int s, e;
	double v;
}a[MAX];

struct Point {
	double x,y;
}p[N];

int pa[MAX] ,n;
bool cmp(Edge a, Edge b) {
	return a.v < b.v;
}

void init() {
	for(int i = 0; i < n; i++) {
		pa[i] = i;
	}
}
int getPa(int x) {
	if(x == pa[x]) {
		return x;
	}else {
		return getPa(pa[x]);
	}
}
double dis(Point a,Point b) {
	return sqrt( (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
int main() {
	int t;
	scanf("%d",&t);
	while(t--) {
		scanf("%d",&n);
		for(int i = 0; i < n; i++) {
			scanf("%lf%lf",&p[i].x,&p[i].y);	
		}
		
		int tot = 0; //总边数为n×(n-1)/2条
		for(int i = 0; i < n; i++) {
			for(int j = i+1; j < n; j++) {
				a[tot].v = dis(p[i] ,p[j]);
				a[tot].s = i;
				a[tot].e = j;
				tot++;
			}
		}
		init(); //初始化并查集

		sort(a,a+tot,cmp);

		double sum = 0;
		int num = 0, k, g; //k,g用于记录起始点和终点所在的集合
		
		for(int i = 0; i < tot && num < n-1; i++) {
			k = getPa(a[i].s);
			g = getPa(a[i].e);
			if(k != g) {
				pa[g] = k;
				sum += a[i].v;
				num++;
			}
		}
		printf("%.2lf\n",sum);

		if(t) {
			printf("\n");
		}
	}
	return 0;
}

你可能感兴趣的:(uva,Freckles,10034)