The Unique MST POJ - 1679 (非严格次小生成树)

题目链接
题目大意:给定无向图,判断最小生成树是否唯一。

思路:求非严格的最小生成树,如果代价等于最小生成树,说明最小生成树不唯一,反之唯一。

非严格次小生成树求法:在求出最小生成树的基础上,枚举每一条没有加进MST里的边<i,j>,把他加进去得到一个环,再删去这个环原先的最长的边(即:MST中i到j路径上的最长边,通过树形dp 维护maxedge[i][j],记录mst中i j路径上最大边权值),替换之后得到的所有权值和中 最小的就是次小生成树了。

(但是这样得到的是非严格的次小生成树,因为这个环原先的最长的边有可能和加进去的边权值相等,

最终得到的次小生成树的代价可能等于MST,即:非严格的次小生成树。
严格次小生成树还需要维护mst中i j路径上次大边权值,替换的时候如果<i,j>路径上最大边权值和<i,j>边权相等,就删去次大值。

这题求非严格的次小生成树就好了。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
using namespace std;
const int maxn = 2e2+7;
const int INF = 1e9+7;
//求次小生成树 
//在求出最小生成树的基础上,枚举每一条没有加进MST里的边,加进去得到带环的树,
//把得到的环里的最长的边删去,就得到一个生成树的权值了,枚举的最小结果就是次小生成树的代价
struct node
{
    int d;
    int v;
    friend bool operator < (const node &a,const node &b)
    {
        return a.d > b.d;
    }
}h,t;
priority_queue<node> q;
int n,m;
int g[maxn][maxn];
int dist[maxn];
bool vis[maxn];
int pre[maxn];//最小生成树中该顶点的父节点
bool used[maxn][maxn];//i->j这条边有没有用过
int maxedge[maxn][maxn];//i j路径上的最大边 利用树上dp更新 
//转移方程 maxedge[i][j]=max(maxedge[i][fa[j]],dist[j])
void init()
{
    fill(g[0],g[0]+maxn*maxn,INF);
    memset(vis,0,sizeof(vis));
    while(q.size()) q.pop();
    fill(dist,dist+maxn,INF);
    memset(pre,0,sizeof(pre));
    memset(maxedge,0,sizeof(maxedge));
    memset(used,0,sizeof(used));
}
int prim()
{
    int ans=0;
    dist[1]=0;
    t.d=0,t.v=1;
    q.push(t);
    while(q.size())
    {
        h=q.top(),q.pop();
        if(vis[h.v]) continue;
        vis[h.v]=1;//入树
        ans+=h.d;
        used[h.v][pre[h.v]]=used[pre[h.v]][h.v]=1;//边入树
        for(int i=1;i<=n;i++)
        {
            if(vis[i])//i已经在mst里面
            {
                //更新h.v与i的路径上最大边
                maxedge[h.v][i]=maxedge[i][h.v]=max(maxedge[i][pre[h.v]],h.d);
            }
            if(!vis[i] && dist[i] > g[h.v][i]) 
            {
                dist[i] = g[h.v][i];
                pre[i]=h.v;
                t.d=dist[i],t.v=i;
                q.push(t);
            }
        }
    }
    return ans;
}
void solve()
{
    int mst=prim();
    int sec_mst=INF;
    for(int i=1;i<=n;i++)
    {
        for(int j=1+i;j<=n;j++)
        {
            if(g[i][j]==INF || used[i][j]) continue;//必须是mst中没有的边
            sec_mst=min(sec_mst,mst-maxedge[i][j]+g[i][j]);
        }
    }
    if(mst == sec_mst) printf("Not Unique!\n");
    else printf("%d\n",mst);
}
int main()
{
    int T;
    cin>>T;
    for(int tt=1;tt<=T;tt++)
    {
        init();
        scanf("%d %d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int v,u,w;
            scanf("%d %d %d",&v,&u,&w);
            g[u][v]=g[v][u]=min(g[u][v],w);
        }
        solve();
    }   
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值