poj-1679-纯-次小生成树

这题是输入一些边然后判断这些边的最小生成树是否唯一,那么我们可以找次小生成树,看看和最小生成树权值是否相同。这里是基于kruskal的次小生成树模板

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <stack>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#define INF 0x3f3f3f3f

using namespace std;

const int maxn=1e2;
struct P
{
    int x,y,cost,vis;//vis标记是否是最小生成树的边
    bool operator<(const P &a)const
    {
        return cost<a.cost;
    }
} p[maxn*maxn+2];

int t,n,m;
int f[maxn];
vector<int> g[maxn];
int dis[maxn][maxn];

int Find(int x)
{
    if(x==f[x]) return x;
    else return f[x]=Find(f[x]);
}

void kruskal()
{
    for(int i=0; i<=n; i++)
    {
        f[i]=i;
        g[i].push_back(i);
    }
    sort(p+1,p+1+m);
    int MST=0;
    for(int i=1; i<=m; i++)//这个循环可以在边界条件上加一个num<n-1,用来剪枝,因为最小生成树只有n-1条边,再往后没有意义
    {
        int a=Find(p[i].x);
        int b=Find(p[i].y);
        if(a!=b)
        {
            for(int j=0; j<g[a].size(); j++)//这两层循环是用来找出成环前已经连接的点之间的最大的边,g[a]表示已经添加进最小生成树的点集,g[b]表示正要添加
            {//到最小生成树的点集,连起来后a,b中的点两两可达,两两间最大边是当前加进去的边
                for(int k=0; k<g[b].size(); k++)
                {
                    int x=g[a][j];
                    int y=g[b][k];
                    dis[x][y]=dis[y][x]=p[i].cost;//更新最大的边
                }
            }
            for(int k=0; k<g[b].size(); k++) g[a].push_back(g[b][k]);//这个地方是把刚加进来的点加入当前树的点集中
            f[b]=a;//一定注意这个地方和前面是关联的,不能f[a]=b
            p[i].vis=1;
            MST+=p[i].cost;
        }
    }
    int ans=INF;
    for(int i=1; i<=m; i++)//枚举除了最小生成树剩下的边,把它放进最小生成树会形成环,然后去掉当前环里面的最大的边,然后找出这些生成树里面最小的,就是次小的
        if(!p[i].vis)
            ans=min(ans,MST+p[i].cost-dis[p[i].x][p[i].y]);
    if(ans==MST) printf("Not Unique!\n");//这题是严格的次小,即权值相等就是不唯一,还有不严格的次小,权值只有大于最小生成树才是次小生成树
    else printf("%d\n",MST);
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].cost);
            p[i].vis=0;
        }
        kruskal();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值