判断最小生成树是否唯一

我们知道在构造最小生成树的时候有可能会选择不同的边,这样构造的最小生成树不相同,但是最小生成树的权是唯一的!

毫无疑问,无向图中存在相同权值的边是最小生成树不唯一的必要条件(但不是充分条件)。正因为如此,如果无向图中各边的权值都不相同,那么在用Kruskal算法构造最小生成树时,选择的方案是唯一的。

这里给出判定最小生成树唯一的算法思路:

1.对图中的每一条边,扫描其他边,如果存在相同权值的边,则对此边做标记。

2.然后使用Kruskal(或者prim)算法求出最小生成树。

3.如果这时候的最小生成树没有包含未被标记的边,即可判定最小生成树唯一。如果包含了标记的边,那么依次去掉这些边,再求最小生成树,如果求得的最小生成树的权值和原来的最小生成树的权值相同,即可判断最小生成树不唯一。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 const int N=11000;
  7 const int M=15005;
  8 int n,m,cnt;
  9 int parent[N];
 10 int flag;
 11 struct edge
 12 {
 13     int u;
 14     int v;
 15     int w;
 16     int equals;///是否存在与该边权值相同的其他边
 17     int used;///在第一次求得的MST中是否包含改变
 18     int del;///边是否删除的标志
 19 } edg[N];
 20 int  cmp(edge x,edge y)
 21 {
 22     return x.w<y.w;
 23 }
 24 void init()
 25 {
 26     int i;
 27     for(i=0; i<=N; i++)
 28     {
 29         parent[i]=i;
 30     }
 31 }
 32 int Find(int x)
 33 {
 34     if(parent[x] != x)
 35     {
 36         parent[x] = Find(parent[x]);
 37     }
 38     return parent[x];
 39 }//查找并返回节点x所属集合的根节点
 40 void Union(int x,int y)
 41 {
 42     x = Find(x);
 43     y = Find(y);
 44     if(x == y)
 45     {
 46         return;
 47     }
 48     parent[y] = x;
 49 }//将两个不同集合的元素进行合并
 50 int Kruskal()
 51 {
 52     init();
 53     int sum=0;
 54     int num=0;
 55     for(int i=0; i<m; i++)
 56     {
 57         if(edg[i].del==1)
 58         {
 59             continue;
 60         }
 61         int u=edg[i].u;
 62         int v=edg[i].v;
 63         int w=edg[i].w;
 64         if(Find(u)!=Find(v))
 65         {
 66             sum+=w;
 67             if(!flag)
 68             {
 69                 edg[i].used=1;
 70             }
 71             num++;
 72             Union(u,v);
 73         }
 74         if(num>=n-1)
 75         {
 76             break;
 77         }
 78     }
 79     return sum;
 80 }
 81 int main()
 82 {
 83     int t,d;
 84     int i,j;
 85     int counts1,counts2;
 86     int flag2;
 87     scanf("%d",&t);
 88     while(t--)
 89     {
 90         counts1=0;
 91         scanf("%d%d",&n,&m);
 92         for(i=0; i<m; i++)
 93         {
 94             scanf("%d%d%d",&edg[i].u,&edg[i].v,&edg[i].w);
 95             edg[i].del=0;
 96             edg[i].used=0;
 97             edg[i].equals=0;//一开始这个地方eq没有初始化,WA了好几发,操
 98         }
 99         for(i=0; i<m; i++)
100         {
101             for(j=0; j<m; j++)
102             {
103                 if(i==j)
104                 {
105                     continue;
106                 }
107                 if(edg[i].w==edg[j].w)
108                 {
109                     edg[i].equals=1;
110                 }
111             }
112         }
113         sort(edg,edg+m,cmp);
114         flag=0;
115         counts1=Kruskal();
116         flag=1;
117         flag2=0;
118         for(i=0; i<m; i++)
119         {
120             if(edg[i].used&&edg[i].equals)
121             {
122                 edg[i].del=1;
123                 counts2=Kruskal();//printf("%d %d\n",i,s);
124                 if(counts2==counts1)
125                 {
126                     flag2=1;
127                     printf("Not Unique!\n");
128                     break;
129                 }
130                 edg[i].del=0;
131             }
132         }
133         if(!flag2)
134         {
135             printf("%d\n",counts1);
136         }
137     }
138     return 0;
139 }

 

转载于:https://www.cnblogs.com/wkfvawl/p/9845689.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值