题目大意:
在"Dick Van Dyke"的某一集中,小 Richie 尝试把他爸爸背上的斑点连成独立钟的图案。但是其中一个斑点是伤疤,所以 Richie 的尝试失败了。
把 Dick 的背看成一个平面,上面有不同位置的斑点 (x,y)。你的任务是要告诉 Richie 应该怎样连接所有点,使得所用的墨水最少。Richie 总是用直线段连接两个点,在画不同线段之间可以将笔提起。当Richie 结束画线时,任何一个斑点必须能够经过所画的线到达任何另外一个斑点。对于每组数据,你的程序要输出一个保留两位小数的实数,表示将所有斑点连接起来所需线段的最小总长度。相邻两组数据的输出之间应该有一个空行。
解析:
最小生成树的问题,先将点和点间的距离转换为包含起始点和终点、以及权重的边,然后套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; }