两道最小生成树范例,Kruskal解法-以边为主体扩展最小生成树,需要利用并查集。
ZOJ1203-Swordfish
题意:求n个给定平面坐标的城市中的一条平面距离上的最短路长(保留两位小数)
题解:这道题数据不是很大,用Kruskal和Prim等算法都能够做。
Kruskal的算法思路是以边为主体扩展结点,即先选取权值最少的边,将两个不连通的端点加入到同一集合中(使其连通),舍去该边,接着找权值次小的,以此类推…
如果两个端点连通,则直接舍去该边。
因此可以先将所有边依据权值大小排序后,然后依次查找即可,为了较快地表示两个端点连通(属于同一集合),需要用到并查集的路径压缩。
1 //剑鱼行动-Kruskal 2 //找出n个给定平面坐标的城市中的一条最短路长(保留两位小数) 3 //Time:0Ms Memory:432K 4 #include<iostream> 5 #include<cstring> 6 #include<cstdio> 7 #include<cmath> 8 #include<algorithm> 9 using namespace std; 10 11 #define MAX 101 12 #define POW2(x) ((x)*(x)) 13 14 struct City { 15 double x, y; 16 }c[MAX]; 17 18 struct Edge { 19 int u, v; //端点 20 double road; 21 friend bool operator < (Edge e1, Edge e2) { return e1.road < e2.road; } 22 }e[MAX*MAX]; 23 24 int n, m; 25 int fa[MAX]; 26 double minroad; 27 28 int Find(int x) 29 { 30 return fa[x] < 0? x : fa[x] = Find(fa[x]); //查根+路径压缩 31 } 32 33 //加权法则合并 34 void Union(int r1,int r2) 35 { 36 int num = fa[r1] + fa[r2]; //集合元素总数-以负数计数 37 if (fa[r1] > fa[r2]) //r2集合元素多 38 { 39 fa[r1] = r2; 40 fa[r2] = num; 41 } 42 else { //r1集合元素多 43 fa[r2] = r1; 44 fa[r1] = num; 45 } 46 } 47 48 void kruskal() 49 { 50 minroad = 0; 51 memset(fa, -1, sizeof(fa)); 52 int num = 0; //已用结点数 53 for (int i = 0; i < m; i++) 54 { 55 int r1 = Find(e[i].u); 56 int r2 = Find(e[i].v); 57 if (r1 == r2) continue; 58 minroad += e[i].road; 59 Union(r1, r2); 60 num++; 61 if (num == n - 1) break; 62 } 63 } 64 65 int main() 66 { 67 int cas = 0; 68 while (scanf("%d", &n), n) 69 { 70 for (int i = 0; i < n; i++) 71 scanf("%lf%lf", &c[i].x, &c[i].y); 72 73 m = 0; 74 for (int i = 0; i < n; i++) 75 for (int j = i + 1; j < n; j++) 76 { 77 double d = sqrt(POW2(c[i].x - c[j].x) + POW2(c[i].y - c[j].y)); 78 e[m].road = d; 79 e[m].u = i; 80 e[m++].v = j; 81 } 82 83 sort(e, e + m); 84 85 kruskal(); 86 if (cas) printf("\n"); //博主在此PE过= = 87 printf("Case #%d:\n", ++cas); 88 printf("The minimal distance is: %.2lf\n", minroad); 89 } 90 91 92 return 0; 93 }
POJ1861(ZOJ1542)-Network
题意:找出可连通网络中最长的网线长度在所有方案中最小的方案,依次输出使用的最长边长,边数及各边端点信息,Special Judge
题解:Sample貌似有可能是是在提醒Special Judge,也有可能是出错了,但是这个Sample确实有点…误导人…
读英文需要注意理解题意 //the maximum length of a single cable is minimal.
其实就是在求一个最小生成树,思路参照上题,注意记录使用过的边。
1 //Kruskal-除了样例和英文有点坑 2 //Time:79Ms Memory:348K 3 #include<iostream> 4 #include<cstring> 5 #include<cstdio> 6 #include<algorithm> 7 using namespace std; 8 9 #define MAXN 1005 10 #define MAXM 15005 11 12 struct Edge { 13 int u, v; 14 int d; 15 friend bool operator < (Edge e1, Edge e2) { return e1.d < e2.d; } 16 }e[MAXM]; 17 18 int n, m; 19 int fa[MAXN]; 20 int used[MAXN]; //记录使用过的边 21 22 //路径压缩+查父 23 int Find(int x) 24 { 25 return fa[x] < 0 ? x : fa[x] = Find(fa[x]); 26 } 27 28 //加权合并 29 void Union(int r1,int r2) 30 { 31 int num = fa[r1] + fa[r2]; 32 if (fa[r1] < fa[r2]) 33 { 34 fa[r2] = r1; 35 fa[r1] = num; 36 } 37 else { 38 fa[r1] = r2; 39 fa[r2] = num; 40 } 41 } 42 43 void kruskal() 44 { 45 memset(fa, -1, sizeof(fa)); 46 int sum = 0; 47 int i = 0; 48 for (; i < m; i++) 49 { 50 int r1 = Find(e[i].u); 51 int r2 = Find(e[i].v); 52 if (r1 == r2) continue; 53 Union(r1, r2); 54 used[sum++] = i; 55 if (sum == n - 1) break; 56 } 57 printf("%d\n", e[i].d); 58 printf("%d\n", sum); 59 for (int j = 0; j < sum; j++) 60 printf("%d %d\n", e[used[j]].u, e[used[j]].v); 61 } 62 63 int main() 64 { 65 scanf("%d%d", &n, &m); 66 for (int i = 0; i < m; i++) 67 scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].d); 68 sort(e, e + m); 69 kruskal(); 70 71 return 0; 72 }