我們知道在構造最小生成樹的時候有可能會選擇不同的邊,這樣構造的最小生成樹不相同,但是最小生成樹的權是唯一的!
毫無疑問,無向圖中存在相同權值的邊是最小生成樹不唯一的必要條件(但不是充分條件)。正因為如此,如果無向圖中各邊的權值都不相同,那么在用Kruskal算法構造最小生成樹時,選擇的方案是唯一的。
這里給出判定最小生成樹唯一的算法思路:
1.對圖中的每一條邊,掃描其他邊,如果存在相同權值的邊,則對此邊做標記。
2.然后使用Kruskal(或者prim)算法求出最小生成樹。
3.如果這時候的最小生成樹沒有包含未被標記的邊,即可判定最小生成樹唯一。如果包含了標記的邊,那么依次去掉這些邊,再求最小生成樹,如果求得的最小生成樹的權值和原來的最小生成樹的權值相同,即可判斷最小生成樹不唯一。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=11000; const int M=15005; int n,m,cnt; int parent[N]; int flag; struct edge { int u; int v;//頂點 int w;//權值 int equals;//是否存在與該邊權值相同的其他邊 int used;//在第一次求得的MST中是否包含該邊。1包含,0不包含 int del;//邊是否刪除的標志 } edg[N]; int cmp(edge x,edge y) { return x.w<y.w; } void init() { int i; for(i=0; i<=N; i++) { parent[i]=i; } } int Find(int x) { if(parent[x] != x) { parent[x] = Find(parent[x]); } return parent[x]; }//查找並返回節點x所屬集合的根節點 void Union(int x,int y) { x = Find(x); y = Find(y); if(x == y) { return; } parent[y] = x; }//將兩個不同集合的元素進行合並 int Kruskal() { init(); int sum=0; int num=0; for(int i=0; i<m; i++) { if(edg[i].del==1) { continue; } int u=edg[i].u; int v=edg[i].v; int w=edg[i].w; if(Find(u)!=Find(v)) { sum+=w; if(flag) { edg[i].used=1; } num++; Union(u,v); } if(num>=n-1) { break; } } return sum; } int main() { int t; int i,j; int counts1,counts2,flag2; scanf("%d",&t); while(t--) { counts1=0; scanf("%d%d",&n,&m); for(i=0; i<m; i++) { scanf("%d%d%d",&edg[i].u,&edg[i].v,&edg[i].w); edg[i].del=0; edg[i].used=0; edg[i].equals=0;//一開始這個地方eq沒有初始化,WA了好幾發 } for(i=0; i<m; i++)//標記相同權值的邊 { for(j=0; j<m; j++) { if(i==j) { continue; } if(edg[i].w==edg[j].w) { edg[i].equals=1; } } } sort(edg,edg+m,cmp); flag=1; counts1=Kruskal();//第1次求MST flag=0; flag2=1; for(j=0; j<m; j++) { if(edg[j].used&&edg[j].equals)//在第一次MST中包含該邊,並且該邊具有權值相同的邊 { edg[j].del=1;//刪除掉該邊,進行第二次MST counts2=Kruskal();//printf("%d %d\n",i,s); if(counts2==counts1) { flag2=0; printf("Not Unique!\n"); break; } edg[j].del=0;//恢復被刪掉的邊 } } if(flag2) { printf("%d\n",counts1); } } return 0; }