【POJ1679】The Unique MST

  1 //ver1: Kruskal+暴力枚举删除边
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<algorithm>
  5 using namespace std;
  6 int t,n,m,first;
  7 int a,ok,minx,p[101];
  8 struct edge{
  9     int v1,v2,d,v;  //v标记MST中的每一条边 
 10 }e[10001];
 11 int findp(int x){
 12     return x==p[x]?x:p[x]=findp(p[x]);
 13 }
 14 int cmp(edge a,edge b){
 15     return a.d<b.d;
 16 }
 17 void init(){
 18     cin>>n>>m;
 19     ok=1;first=1;
 20     for(int i=0;i<m;i++){
 21         cin>>e[i].v1>>e[i].v2>>e[i].d;
 22         e[i].v=0;
 23     }
 24     sort(e,e+m,cmp);
 25 }
 26 int Kruskal(){
 27     for(int i=1;i<=n;i++) 
 28         p[i]=i;
 29     a=n; int sum=0;
 30     for(int i=0;i<m;i++){
 31         int p1=findp(e[i].v1),p2=findp(e[i].v2);
 32         if(p1!=p2){
 33             sum+=e[i].d;
 34             a--;
 35             p[p1]=p2;
 36             if(first) e[i].v=1;  //边i属于MST 
 37         }
 38         if(a==1) break;
 39     }
 40     if(a==1) return sum;
 41     return -1;
 42 }
 43 int main(){
 44     cin>>t;
 45     while(t--){
 46         init();
 47         minx=Kruskal();        //求MST 
 48         first=0;               //注意MST已经求过了 
 49         for(int i=0;i<m;i++){
 50             if(e[i].v){        //删除MST中的一条边,再添加一条边形成连通图 
 51                 int t=e[i].v1; //把这条件的两个点变成一个点,从而在下面的Kruskal中不会再选择这条边 
 52                 e[i].v1=e[i].v2;
 53                 int sum=Kruskal();
 54                 if(sum==minx) {ok=0;break;}
 55                 e[i].v1=t;
 56             } 
 57         }
 58         if(ok) cout<<minx<<endl;
 59         else cout<<"Not Unique!\n";
 60     }
 61 }
 62 //ver2:Kruskal+判断是否存在权值相等且连接的等价集合相同
 63 #include<cstdio>
 64 #include<iostream>
 65 #include<algorithm>
 66 using namespace std;
 67 int t,n,m,a,minx,ok,p[101];
 68 struct edge{
 69     int x,y,w;
 70 }e[10001];
 71 int cmp(edge a,edge b){
 72     return a.w<b.w;
 73 }
 74 int findp(int x){
 75     return x==p[x]?x:p[x]=findp(p[x]);
 76 }
 77 void init(){
 78     minx=0;ok=1;
 79     scanf("%d%d",&n,&m); a=n;
 80     for(int i=1;i<=n;i++) p[i]=i;
 81     for(int i=0;i<m;i++)
 82         scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
 83     sort(e,e+m,cmp);
 84 }
 85 void Kruskal(){
 86     for(int i=0;i<m;i++){
 87         int p1=findp(e[i].x),p2=findp(e[i].y);
 88         if(p1!=p2){
 89             for(int j=i+1;j<m;j++)
 90                 if(e[i].w==e[j].w){
 91                     //判断链接的是否是相同的集合
 92                     int p3=findp(e[j].x),p4=findp(e[j].y);
 93                     if(p3==p1&&p4==p2||p3==p2&&p4==p1){
 94                         ok=0;
 95                         return;
 96                     } 
 97                 }
 98                 else break;//由于e[]是有序的,发现权值不同,立即退出循环
 99             p[p1]=p2;
100             minx+=e[i].w;
101             a--;
102         }
103         if(a==1) break;
104     }
105 }
106 int main()
107 {
108     scanf("%d",&t);
109     while(t--){
110         init();
111         Kruskal();
112         if(ok) printf("%d\n",minx);
113         else printf("Not Unique!\n");
114     }
115 }
116 //ver3: 可以前后求两次MST,在权值相同的情况下,第一次边编号小的优先选,第二次边编号大的优先选,
117 //判断两次MST是否相同即可(一是最短路径和是否相等,二是所有的边是否相同) 

判断最小生成树是否唯一

posted @ 2017-11-20 22:15  sulley  阅读(206)  评论(0编辑  收藏  举报