POJ 1679 判断最小树是否唯一

题意:
     判断最小树是否唯一。
思路:

     我用了两种方法,主要就是好久没敲了,找个水题练练手,第一种就是先一遍最小生成树,然后枚举最小生成树上的每一条边,然后取消这条边,在跑一遍最小生成树,就这样一直跑最小生成树,如果找到了一颗和之前的那个一样的,那么就是不唯一,第二种方法也是先最小树,然后枚举,在枚举的时候不是继续重新跑,而是断开当前边,把树分成两个集合<两次深搜实现>,然后在枚举这连个集合之间是否可以找到一个可以代替当前枚举的最小树的边,实现复杂度的话应该是第二种快点,但理论上也快不多少,只是为了练练手,在多说一句,第二种方法和求次小树的思路有点像,但是次小树可以再这个上面进行dp优化,其实这个题目也可以直接判断次小树是否等于最小树。好像有点说多了。


#include<stdio.h>
#include<string.h>
#include<algorithm>

#define N 110

using namespace std;

typedef struct
{
   int x ,y ,c;
}EDGE;

typedef struct
{
   int to ,next;
}STAR;

EDGE edge[N*N];
STAR E[N*N];
int  map[N][N] ,mer[N] ,mark[N];
int list[N] ,tot ,mst[N];
int L[N] ,R[N] ,ll ,rr;

void add(int a, int b)
{
   E[++tot].to = b;
   E[tot].next = list[a];
   list[a] = tot;
}

int finds(int x)
{
   return x == mer[x] ? x : mer[x] = finds(mer[x]);
}

int minn(int x ,int y)
{
   return x < y ? x : y;
}

bool camp(EDGE a ,EDGE b)
{
   return a.c < b.c;
}

int MST(int n ,int m)
{
   for(int i = 1 ;i <= n ;i ++) mer[i] = i;
   int Ans = 0 ,sum = 0;
   memset(list ,0 ,sizeof(list)) ,tot = 1;
   for(int i = 1 ;i <= m ;i ++)
   {
      int xx = finds(edge[i].x);
      int yy = finds(edge[i].y);
      if(xx != yy) 
      {
         Ans += edge[i].c ,sum ++;
         mer[xx] = yy;
         mst[sum] = i;
         add(edge[i].x ,edge[i].y);
         add(edge[i].y ,edge[i].x);
      }
      if(sum == n - 1) break;
   }
   return Ans;
}

void DFS1(int x)
{
   for(int k = list[x] ;k ;k = E[k].next)
   {
      int to = E[k].to;
      if(mark[to]) continue;
      mark[to] = 1;
      L[++ll] = to;
      DFS1(to);
   }
}

void DFS2(int x)
{
   for(int k = list[x] ;k ;k = E[k].next)
   {
      int to = E[k].to;
      if(mark[to]) continue;
      mark[to] = 1;
      R[++rr] = to;
      DFS2(to);
   }
}


int main ()
{
   int t ,n ,m ,i ,j ,mk;
   scanf("%d" ,&t);
   while(t--)
   {
       scanf("%d %d" ,&n ,&m);
       for(i = 1 ;i <= n ;i ++)
       for(j = 1 ;j <= n ;j ++)
       map[i][j] = 100000000;
       for(i = 1 ;i <= m ;i ++)
       {
          scanf("%d %d %d" ,&edge[i].x ,&edge[i].y ,&edge[i].c); 
          int x = edge[i].x ,y = edge[i].y;
          map[x][y] = map[y][x] = minn(map[x][y] ,edge[i].c);
      }
      sort(edge + 1 ,edge + m + 1 ,camp);
      int Ans = MST(n ,m);
      if(m == n - 1)
      {
         printf("%d\n" ,Ans);
         continue;
      }
      mk = 0;
      for(i = 1 ;i < n && !mk;i ++)
      {
         memset(mark ,0 ,sizeof(mark));
         int l = edge[mst[i]].x ,r = edge[mst[i]].y;
         mark[l] = mark[r] = 1;
         ll = rr = 0;
         L[++ll] = l ,R[++rr] = r;    
         DFS1(l) ,DFS2(r);
         for(int j = 1 ;j <= ll && !mk;j ++)
         {
            for(int k = 1 ;k <= rr && !mk ;k ++)
            {
               if(L[j] == edge[mst[i]].x && R[k] == edge[mst[i]].y || L[j] == edge[mst[i]].y && R[k] == edge[mst[i]].x)
               continue;
               if(map[L[j]][R[k]] == edge[mst[i]].c) mk = 1;
            }
         }
      }
      mk ? printf("Not Unique!\n"): printf("%d\n" ,Ans);
   }
   return 0;
}
         
      

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值