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

哪有什么传送门….

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;
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值