[kuangbin带你飞]专题六 最小生成树---poj1679( kruskal实现次小生成树)

K - The Unique MST POJ - 1679

[kuangbin带你飞]专题六 最小生成树---poj1679( kruskal实现次小生成树)_第1张图片
[kuangbin带你飞]专题六 最小生成树---poj1679( kruskal实现次小生成树)_第2张图片

思路1(动规思想):

  1. 先求出最小生成树。
  2. 枚举每条不在最小生成树上的边,并把这条边放到生成树里,然后就一定会形成环,从环中取出一条边(生成树再去掉里面的最长边,在求最小生成树时,用dp【u】【v】去维护)。
#include 
#include 
#include 
#include 
#include 
#define fzhead EDGE(int _from, int _to, int _val, int _next,int _vis)
#define fzbody from(_from), to(_to), val(_val), next(_next) ,vis(_vis)
#define mst(x,a) memset(x,a,sizeof(x))
#define pb push_back
#define mp make_pair
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int maxn=100+10;
const int maxm=1e4+10;
vector<int>p[maxn];
struct EDGE
{
     
    int from,to,next,val;
    int vis;
    EDGE(){
     }
    fzhead:fzbody{
     }
    bool operator <(const EDGE&a)const
    {
     
        return val<a.val;
    }
}e[maxm];
int cnt,n,m;
int head[maxn], f[maxn];//vis[maxm],
int dp[maxn][maxn];
void add(int bg, int to, int val)
{
     
    e[++cnt]=EDGE(bg,to,val,head[bg],0);
    head[bg]=cnt;
}
int find(int x)
{
     
    if(x==f[x])return f[x];
    return f[x]=find(f[x]);
}
int main()
{
     
    int t;
    scanf("%d", &t);
    while(t--)
    {
     
        cnt=0;
        mst(head,-1);mst(e,0);mst(dp,0);//mst(vis,0);
        scanf("%d%d", &n,&m);
        int a,b,val;
        For(i,1,n)
        {
     
            f[i]=i;
            p[i].clear();
            p[i].pb(i);
        }
        For(i,1,m)
        {
     
            scanf("%d%d%d",&a,&b,&val);
            add(a,b,val);
        }
        sort(e+1,e+1+cnt);
        ll ans=0;
        int cnt1=0;
        For(i,1,cnt)
        {
     
            a=find(e[i].from);b=find(e[i].to);
            if(a==b)continue;
            ans+=e[i].val;
            f[a]=b;
            e[i].vis=1;
            //vis[++cnt1]=i;
            int len1=p[a].size();
            int len2=p[b].size();
            For(j,0,len1-1)
            {
     
                For(k,0,len2-1)dp[p[a][j]][p[b][k]]=dp[p[b][k]][p[a][j]]=e[i].val;
                //因为边权说排序了的,所以越往后,边权就越大,
                //直接更新集合里的点到各点之前的最大边权
            }
            For(j,0,len1-1)p[b].pb(p[a][j]);
            //把祖先a集合里的所有点,合并到集合b里去。
            if(cnt1==n-1)break;
        }
        ll res=INF;
        int flag=1,tot=1;
        For(i,1,cnt)
        {
     
            if(!e[i].vis)
            res=min(res,ans+e[i].val-dp[e[i].from][e[i].to]);
        }
        if(res>ans)printf("%lld\n",ans);
        else printf("Not Unique!\n");
    }
    return 0;
}

思路2(暴力):

要你求次小生成树,看次小生成树是不是等于最小生成树,如果等于就NOT unique。

  1. 可以先求出最小生成树。(在求的时候记录用了那条边)
  2. 之后枚举不用这条边的次小生成树。
#include 
#include 
#include 
#include 
#define fzhead EDGE(int _from, int _to, int _val, int _next)
#define fzbody from(_from), to(_to), val(_val), next(_next)
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
typedef long long ll;
const int maxn=100+10;
const int maxm=1e4+10;
struct EDGE
{
     
    int from,to,next,val;
    EDGE(){
     }
    fzhead:fzbody{
     }
    bool operator <(const EDGE&a)const
    {
     
        return val<a.val;
    }
}e[maxm];
int cnt,n,m;
int head[maxn],vis[maxm], f[maxn];
void add(int bg, int to, int val)
{
     
    e[++cnt]=EDGE(bg,to,val,head[bg]);
    head[bg]=cnt;
}
int find(int x)
{
     
    if(x==f[x])return f[x];
    return f[x]=find(f[x]);
}
int main()
{
     
    int t;
    scanf("%d", &t);
    while(t--)
    {
     
        cnt=0;
        mst(head,-1);mst(e,0);mst(vis,0);
        scanf("%d%d", &n,&m);
        int a,b,val;
        For(i,1,n)f[i]=i;
        For(i,1,m)
        {
     
            scanf("%d%d%d",&a,&b,&val);
            add(a,b,val);
        }
        sort(e+1,e+1+cnt);
        ll ans=0;
        int cnt1=0;
        For(i,1,cnt)
        {
     
            a=find(e[i].from);b=find(e[i].to);
            if(a==b)continue;
            ans+=e[i].val;
            f[a]=b;
            vis[++cnt1]=i;
            if(cnt1==n-1)break;
        }
        //第一次求
        ll tmp=0;
        int flag=1,tot=1;
        //cout<
        For(i,1,cnt1)
        {
     
            tmp=0;tot=1;
            For(j,1,n)f[j]=j;
            For(j,1,cnt)
            {
     
                if(j==vis[i])continue;//枚举不在最小生成树的边,再求最小生成树。
                 a=find(e[j].from);b=find(e[j].to);
                if(a==b)continue;
                tmp+=e[j].val;
                f[a]=b;
                tot++;//
            }
          // cout<
            if(tmp==ans&&tot==n){
     flag=0;break;}
        }
        if(flag)printf("%lld\n",ans);
        else printf("Not Unique!\n");
    }
    return 0;
}

你可能感兴趣的:(#,最小生成树走起,kruskal)