题目
分析
题目大意:
求一张图的最小生成树是否唯一,唯一则输出价值,否则输出“Not Unique!”。
思路1:
求一棵最小生成树,再求一棵次小生成树。两者价值相等则不唯一。
思路2:
先求一棵最小生成树。依次枚举树里面每一条边,让它不能被使用,新构建一棵生成树。如果不含此边的生成树(即新求出的这棵)也最小,说明不唯一。
思路2的详细解释在代码部分。
代码
此处为思路2,用kruskal实现。
代码中kruskal函数的参数表示的是不能使用的边的编号。
一开始先求出最小生成树,在函数中特判第一次调用,使其返回求出的价值,并标记生成树中的边。
枚举每一条边,标记它为被删除(即调用一次函数)。如果成功求出(删去此边后依然能生成一棵树),判断是否与最小价值相等。
一旦求出一棵价值也最小的生成树,直接判定为不唯一。否则继续枚举、删边、生成树。
值得注意的是,不要忘记每次跑kruskal时reset并查集的father数组。
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=102;
struct Edge{
int from,to,v;
bool tree;
}e[maxn*maxn];
int fa[maxn];
int T,n,m,ans,cnt;
bool cmp(Edge A,Edge B)
{
return A.v<B.v;
}
void add(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].from=u;
e[cnt].v=w;
e[cnt].tree=false;
}
int Find(int x)
{
if(fa[x]==x) return x;
return fa[x]=Find(fa[x]);
}
int kruskal(int k)
{
for(int i=1;i<=n;i++) fa[i]=i;
int now=0,sum=0;
for(int i=1;i<=cnt;i++)
if(i!=k)
{
int u=e[i].from,v=e[i].to;
int p=Find(u),q=Find(v);
if(p!=q)
{
fa[q]=p;
sum+=e[i].v;
now++;
if(!k) e[i].tree=1;
}
if(now==n-1) break;
}
if(!k) return sum;
if(now==n-1&&sum==ans) return 1;
else return 0;
}
int main()
{
scanf("%d",&T);
while(T--)
{
cnt=0;
scanf("%d%d",&n,&m);
for(int i=1,x,y,z;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
sort(e+1,e+cnt+1,cmp);
ans=kruskal(0);
bool flag=0;
for(int i=1;i<=cnt;i++)
if(e[i].tree)
{
int t=kruskal(i);
if(t==1)
{
printf("Not Unique!\n");
flag=1;
break;
}
}
if(!flag) printf("%d\n",ans);
}
return 0;
}