首页 > 代码库 > UnionFind1308

UnionFind1308

 

给出一系列节点对,判断是不是树。是树的条件是:只有一个根节点,没有指向根节点的边,除了根节点每个节点都只有一条(出度可以不是1,入度必须是1,根节点在最后给出)边指向它

 

第一个 第二个满足条件,第三个不满足,因为8节点入度为2

 

这道题很容易往并查集的方向上想,事实上用并查集也可以解决.但是这个问题有更加简单的解法,不需要使用并查集.

题目给出了有向边的数据,因为所有的顶点都是以有向边端点的形式给出的,所以不存在孤立的点.

如果所有的边能够构成一棵树,那么必然会有:顶点个数 = 边的数目 + 1,另外题目还要求不能有节点入度大于1,因此再加上判断入度是否都1的条件。

因此,只要满足根节点以外节点入度都为1并且nvertices = nedges + 1,就必然是一棵树.

 

#include<iostream>
#define MAX 50000
using namespace std;

int parent[MAX+1],a,b,nedges,nvertices;
bool visit[MAX+1];
int main()
{
int count = 1;
while(scanf("%d%d",&a,&b))
{
 if(a == -1 && b == -1)
 break;
 if(a == 0 && b == 0)
 {
  printf("Case %d is a tree.\n",count);
  count++;
  continue;
 }
       nvertices = 0,nedges = 0;
 bool flag = true;
 memset(parent,0,sizeof(parent));
 memset(visit,false,sizeof(visit));
 while( !(a == 0 && b == 0))
 {
  if(flag == true)
  {
   if(parent[b]>0)
   flag = false; 
   if(visit[a] == false)
   nvertices++,visit[a] = true;
   if(visit[b] == false)
   nvertices++,visit[b] = true;
   nedges++;
   parent[b]=a;
  }
  scanf("%d%d",&a,&b);        
 }
    if(nvertices == nedges + 1 && flag == true)
 printf("Case %d is a tree.\n",count);
 else
 printf("Case %d is not a tree.\n",count);
 count++;
}
}

 

用并查集解决:

总结一下,其实题目要求是就是确定一个边序列是否为树。入度只能为1也是确定是否为树,入度为2或者更多则为图。因此只需要判断既不是图也不是森林(两个或者更多独立的树存在)就可以了。之所以用并查集是因为可以迅速判断一对新来的边是否构成环(即图),因为他们与根节点直接相连,通过判断他们的根节点是否相同就可以判断是否存在环(这两个节点本身存在一条新边。)

#include<iostream>

using namespace std;

const int N = 105;

 

int p[N];

bool flag[N];

 

void make_set(void)

{

    for(int i = 0; i < 105; i++)

    {

        p[i] = i;

        flag[i] = false;

    }

}

 

int find_set(int x){

    return p[x] == x ? x : (p[x] = find_set(p[x]));

}

 

void union_set(int x, int y){

    x = find_set(x);

    y = find_set(y);

    if(x == y) return;

    p[y] = x;

}

 

int main(){

    int x, y, t = 1, first;

    while(scanf("%d %d", &x, &y) != EOF){

        if(x == -1 && y == -1) break;

        if(x == 0 && y == 0){        // 1.空树也是树,果断坑爹了

            printf("Case %d is a tree.\n", t ++);

            continue;

        }

        make_set();

        flag[x] = flag[y] = true;

        first = x;

        bool tree = true;

        if(x == y) tree = false;    //  两个数相等表示指向自己,不是树

 

        else union_set(x, y);

        while(scanf("%d %d", &x, &y) && x != 0){

            flag[x] = flag[y] = true;

            if(find_set(x) == find_set(y)) tree = false;   // 2.两个数的树根相同,现在又出现新边,构成环

            union_set(x, y);//合并

        }

        for(int i = 1; i < 105; i ++)   

//判断是否是森林,即每个已经出现的节点(flag=1)与任意的节点(first节点)是否在同一颗树上。

            if(flag[i] && find_set(i) != find_set(first)) {

                tree = false;

                break;

            }

        if(tree) printf("Case %d is a tree.\n", t ++);

        else printf("Case %d is not a tree.\n", t ++);

    }

    return 0;

}

UnionFind1308