判断最小生成树的唯一性

POJ 1679(第二小生成树)

       对这个题目,给出n,m;n个点和m条边的信息。它采用边的输入方式,所以用Krusal写会好些,至少这有可能是个稀疏图。求第二小生成树的时候,一般采用的是求出最小生成树后,依次删除最小生成树上的每一条边,然后生成n-1次最小生成树,记录下这个过程中的最小的生成树的值,那么这个就是第二小生成树了。那么这道题目,很容易转化成这个形式的,只要判断第二小生成树的值和最小生成树的值是否相等,如果相等就不是唯一的了,没有相等就是唯一的了。

数据结构:maxn=10000

bool flag[maxn+1]; 记录某条边在最小生成树中是否使用过,用过则赋值为1;

struct data{ int x; int y; int w; } sides[maxn+1]; 记录边的起点和终点,还有权值;

int set[110];并查集的数组;

ac代码:

#include<stdio.h>
#include<stdlib.h>
#define maxn 10000
struct data
{
int x;
int y;
int w;
} sides[maxn+1];
int set[110];
bool flag[maxn+1];
int cmp(const void *a,const void *b)
{
return ((data *)a)->w - ((data *)b)->w;
}
int find(int x) //寻找根节点;
{
if(set[x]==x) return x;
set[x]=find(set[x]); //寻找的过程中,更新set[x]的值,路径压缩;
return set[x];
}
int main()
{int k,n,i,j,ans,t,ans1,rx,ry,before,m;
bool bo;//标志是否找到答案;
scanf("%d",&k);
while(k--)
{
   scanf("%d%d",&n,&m);
   bo=0;
   for(i=1;i<=m;i++)
   {
     scanf("%d%d%d",&sides[i].x,&sides[i].y,&sides[i].w);
   }
   qsort(&sides[1],m,sizeof(sides[0]),cmp);   //贪心,排序,按权值;
   for(i=1;i<=n;i++) set[i]=i;//并查集初始化;
   for(i=1;i<=m;i++) flag[i]=0;
   ans=0;
   i=0;
   j=0;
   while(i<n-1)   //求最小生成树
   {
    j++;
    rx=find(sides[j].x);//寻找根节点
    ry=find(sides[j].y);
    if(rx!=ry)
    {
      i++;
      ans+=sides[j].w;
      set[rx]=set[ry]; //合并集合 ;
      flag[j]=1;
    }   
   }
   if(m==n-1){printf("%d\n",ans);continue;}//如果最小生成树的边数等于n-1;那只有一种,就是所求的
   before=1;
   flag[before]=0;   //先才能够flag[1]的边开始删
   for(t=1;t<=n-1;t++) //进行n-1次删边的操作。。
   {
        for(i=1;i<=n;i++) set[i]=i;//并查集初始化;
        i=0;
        j=0;
        ans1=0;
        while(i<n-1)//同样的操作,求次最小生成树。。
        {
         j++;
         if(j!=before)//每次删除一条边。。
         {
            rx=find(sides[j].x);
            ry=find(sides[j].y);
            if(rx!=ry)
            {
               i++;
               ans1+=sides[j].w;
               set[rx]=set[ry];
            }   
        }
       }
       if(ans1==ans) { bo=1; break;}  //如果次最小生成树的总权值等于最小生成树,则不唯一
       for(i=before+1;i<=m;i++)//寻找一条边,赋值before,给下次删除用的。。
         if(flag[i]) { before=i; flag[before]=0; break;} 
   }
   if(bo) printf("Not Unique\n");
   else printf("%d\n",ans);
}
return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值