標籤:
最小產生樹範例,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 10112 #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 }
ACM/ICPC 之 Kruskal範例(POJ1128(ZOJ1083))