您的位置:首页 > 其它

最小生成树是否唯一(次小生成树)

2018-03-07 09:52 169 查看

哪有什么传送门….

Description

给定一个连通无向图,有n个点和m条边。你需要去掉一些边,保留长度最小的边,使得图仍然连通,求保留边的最小长度是多少?

上述问题即为最小生成树问题。为了增加难度,我们还想知道,保留最小边集的方案是否唯一,也就是最小生成树的边是否唯一。如果唯一,输出最小长度;如果不唯一,输出“Not Unique!”。

Input

第一行输入T,表示有T组测试数据。

每组测试数据先输入n,m,然后m行,每行输入ai,bi,ci,表示ai和bi之间有长度为ci的边。

Output

如果最小生成树唯一,输出最小长度;如果不唯一,输出“Not Unique!”。

Sample Input

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

Sample Output

3

Not Unique!

Tips

1<=t<=20 1<=n<=100 边的数量不超过10000 0<=ci<=10000

Solution

如果有次小生成树边长和=最小生成树,则该无向图MST不唯一

对于以求得的MST,将一条未使用的边加入,必形成一个环,删去环上最大边,得到次小生成树

在第二点的基础上再加入一条边,则得到的生成树必等价或更劣,因此只需加入一条边

Code

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
struct JOKER{
int x,y,v;
}e[10007];
int cas,n,m,ans,ans2;
int dis[107],f[107][107],fa[107],a[107][107];
//f[i][j]为最小生成树上i,j之间路径中最大边长
bool p[107][107],vis[107];
bool cmp(JOKER a,JOKER b){
return a.v<b.v;
}
int work(){
int anst=1e9+7;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=j&&!p[i][j])
anst=min(anst,ans-f[i][j]+a[i][j]);
return anst;
}
int main(){
scanf("%d",&cas);
while (cas--){
ans=0;
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
memset(fa,0,sizeof(fa));
memset(p,0,sizeof(p));
memset(f,0,sizeof(f));

scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
a[i][j]=1e9+7;
for (int i=1;i<=m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
a[x][y]=z; a[y][x]=z;
}

vis[1]=1;
for (int i=2;i<=n;i++){dis[i]=a[1][i]; fa[i]=1;}
int mm=n-1; dis[1]=0;
while (mm--){
int v,minn=1e9+7;
for (int i=1;i<=n;i++)
if (vis[i]==0&&dis[i]<minn){minn=dis[i]; v=i;}
vis[v]=1; ans+=minn;
//printf("ans=%d\n",ans);
p[fa[v]][v]=p[v][fa[v]]=1;
for (int i=1;i<=n;i++)
if (vis[i]&&i!=v)
f[v][i]=f[i][v]=max(f[i][fa[v]],dis[v]);
for (int i=1;i<=n;i++)
if (!vis[i]&&a[v][i]<dis[i]){
dis[i]=a[v][i];
fa[i]=v;
}
}

ans2=work();
if (ans2==ans) printf("Not Unique!\n");
else printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: