Unique!!!
TimeLimit:1000MS MemoryLimit:10000K
64-bit integer IO format:%lld
Problem Description
给你一幅N个点无向连通图,求它的一幅最小边权和的生成子图:取N-1条边,能够连通这幅图的所有点,并且边权和最小。
如果这幅最小边权和的子图是唯一存在的,则输出他的最小边权和,否则输出"Not Unique!"。
Input
第一行输入T (1 <= T <= 20),表示有T组测试数据. 对于每一组测试数据,输入N和M (1 <= n <= 100 ,1<=M<=100000 ).
然后有M行,每一行输入三个数字a b c,表示a-b边的权值为c。
注意考虑重边
Output
对于每一组测试案例,输出相应的答案、
SampleInput
2
3 3
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2
SampleOutput
3
Not Unique!
题意:判断最小生成树是否唯一
思路:先求一次MST(最小生成树),判断每条边是否有重边,将结果记录下来,然后依次去掉第一次使用过的且含有重边的边,再求一次最小生成树,如果和原答案一致的话则不唯一。
下面献上我的low逼代码
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 100005;
const int M = 100005;
int fa[M];
bool flag;
int n,m,cnt;
struct node
{
int u,v,w;
int used,del,cover;///标记使用过,取出的边,重边
}a[N];
void init()
{
for(int i=0;i<M;i++)
fa[i]=i;
}
int findx(int x)///并查集基本操作
{
return fa[x]==x?x:fa[x]=findx(fa[x]);
}
void join(int x,int y)
{
int xx=findx(x);
int yy=findx(y);
if(xx!=yy)
fa[yy]=xx;
}
bool cmp(node a,node b)
{
return a.w < b.w;
}
bool same(int x,int y)
{
return findx(x)==findx(y);
}
int kruskal()
{
int sum=0;
int num=0;
init();///每次跑都要初始化一次
for(int i=0;i<m;i++)
{
if(a[i].del==1)///删除(忽略)某条边
continue;
if(!same(a[i].u,a[i].v))
{
sum+=a[i].w;
if(!flag)///第一次跑 记录使用过的边 也就是最小生成树的边
a[i].used=1;
join(a[i].u,a[i].v);
num++;
}
if(num>=n-1)
break;
}
return sum;
}
int main()
{
int t;
cin >> t;
while(t--)
{
init();
cnt=0;
cin >> n >> m;
for(int i=0;i<m;i++)
{
cin >> a[i].u >> a[i].v >> a[i].w;
a[i].used=a[i].del=a[i].cover=0;
}
for(int i=0; i<m; i++)
{
for(int j=0; j<m; j++)
{
if(i==j)
continue;
if(a[i].w==a[j].w)
a[i].cover=1;///如果是重边的标记
}
}
sort(a,a+m,cmp);
flag=0;
cnt=kruskal();///第一次MST
flag=1;
bool f=0;
for(int i=0; i<m; i++)
{
if(a[i].used==1 && a[i].cover==1)///使用过 而且是重边
{
a[i].del=1;///将这条边删除
int s=kruskal();
if(s==cnt)///删除后还是和最小生成树权值相同的话
{
f=1;
printf("Not Unique!\n");
break;
}
a[i].del=0;///将这条边恢复
}
}
if(!f)///唯一MST的话则输出权值
cout << cnt << endl;
}
return 0;
}