1. 程式人生 > >poj 1679 The Unique MST (判斷最小生成樹是否唯一)

poj 1679 The Unique MST (判斷最小生成樹是否唯一)

題意:判斷最小生成樹是否唯一,

若唯一,輸出最小權值和,否則,輸出  Not Unique!

判斷最小生成樹是否唯一的思路:

1、對圖中的每一條邊,掃描其他邊,如果存在相同權值的邊,則對該邊做標記

2、然後用Kruskal演算法或Prim演算法求MST

3、求得MST後,如果該MST中未包含做了標記的邊,即可判斷MST唯一

如果包含做了標記的邊,則依次去掉這些邊的一條邊,再求MST,

如果求得的MST權值和原來的MST的權值一樣,即可判斷MST不唯一。

個人思路是先求最小生成樹,並標記最小生成樹包含的邊,

再依次判斷是否有相同權值的邊,若存在,將最小生成樹的該條邊去掉,

重新求最小生成樹,若得到的權值與原來的最小權值和相等,則不唯一

#include<stdio.h>
#include<algorithm>
#include<string.h>
#define INF 999999
using namespace std;
int n,m,k,f[105],path[105];
struct stu
{
    int u,v,w;
}edge[10010];
int cmp(stu a,stu b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(x!=f[x])
        f[x]=find(f[x]);
    return f[x];
}
int kruskal(int flag)
{
    int i,x,y,sum=0;
    for(i=1;i<=n;i++)
        f[i]=i;
    k=0;
    for(i=1;i<=m;i++){
        if(edge[i].w==INF)
            continue;
        x=find(edge[i].u);
        y=find(edge[i].v);
        if(x!=y){
            sum+=edge[i].w;
            if(flag)
                path[k]=i;  //第一次求最小生成樹時記錄路徑
            k++;
            if(k==n-1)
                break;
            f[x]=y;
        }
    }
    if(k!=n-1)
        sum=-1;
    return sum;
}
int main()
{
    int T,i,j,ans,s,t,flag,num;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        memset(edge,0,sizeof(edge));
        for(i=1;i<=m;i++)
            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
        sort(edge+1,edge+m+1,cmp);
        ans=kruskal(1);
        flag=1;
        num=k;
        for(i=0;i<num&&flag;i++){
            j=path[i];             //依次判斷是否存在與路徑相同權值的邊
            if(edge[j-1].w==edge[j].w||edge[j+1].w==edge[j].w){  
                t=edge[j].w;
                edge[j].w=INF;       //若存在,將其標記為極大值,表示去掉此邊,再求最小生成樹
                s=kruskal(0);
                if(s==ans)
                    flag=0;
                edge[j].w=t;        //將該邊還原為原值
            }
        }
        if(!flag)
            printf("Not Unique!\n");
        else
            printf("%d\n",ans);
    }
    return 0;
}