【并查集专题】

poj 1308 Is It A Tree?

http://poj.org/problem?id=1308

  满足一下条件是树:

 (1)只有一个根;

 (2)一个入度为0的节点,其他节点入度都为1

   

第一个用并查集,第二个搞个数组统计下就OK。

我用set来存点,因为这些点在输入中可能出现多次,用set只会存一次,set方便些啦!

#include <cstdio>
#include <cstdlib>
#include <set>
#include <iterator>
#define N 10001
using namespace std;
                   
set<int> s;
set<int>::iterator iter;
 
int father[N];
int degree[N];
 
void make_set();
int find_set(int x);
void unin(int x,int y);
 
int main()
{
   int i,j,f,a,b,k,zero;
   bool fail;
    
     k = 1 ;
     while(scanf("%d%d",&a,&b) && (a!= -1 && b != -1))
     {
          /*
            空树也是树 
          */
          if( a == 0 && b == 0)
          {
            printf("Case %d is a tree.\n",k++); 
            continue;   
          }
          /*
            每组数据都得清空 set容器 
          */
          s.clear();
           
          s.insert(a);
          s.insert(b);       
          /*
            初始化 
          */  
          make_set();           
          unin(a,b);
           
          ++degree[b];
           
          while(scanf("%d%d",&a,&b) && (a&&b))
          {
             s.insert(a);
             s.insert(b);
             unin(a,b);
             ++degree[b];                                                
          }
         
          f = find_set(father[*s.begin()]);
          zero = 0 ;//统计有几个入度为 0 的点 
          fail = false;
           
          for( iter = s.begin(); iter != s.end(); ++iter)
          {
             /*
               树的每个点的祖先都一样,否则不是树 
             */
              if(f != find_set(father[*iter]))
              {
                   fail = true;
                   break; 
              }      
             /*
               树不能有入度大于 1的节点,否则不是树 
             */
              if(degree[*iter]>1) 
              {
                   fail = true;
                   break;                    
              }
              else
              {
                  /*
                   统计根 
                  */
                  if(degree[*iter] == 0)
                  ++zero;    
              }
               
          }
           
          printf("Case %d ",k++);
          if(fail || zero != 1)
          {
                printf("is not a tree.\n");  
          }
          else
          printf("is a tree.\n");
     }
   return 0;
}
  
void make_set()
{
   int i;
   for(i= 1; i < N; ++i)
   {
      father[i] = i;
      degree[i] = 0;       
   }     
}
 
int find_set(int x)
{
    int t;
    if(x != father[x])
    father[x] = find_set(father[x]);//路径压缩 
    return father[x];
}
/*
  因为是树,合并是有方向的 
*/
void unin(int x,int y)
{
   x = find_set(x);
   y = find_set(y);
   father[y] = x;
}
        

 poj 1611 The Suspects

题目描述:

有很多组学生,在同一个组的学生经常会接触,也会有新的同学的加入。但是SARS是很容易传染的,只要在改组有一位同学感染SARS,那么该组的所有同学都被认为得了SARS。现在的任务是计算出有多少位学生感染SARS了。假定编号为0的同学是得了SARS的。

将每一组第一个同学和每一个同学合并就可以了。
采用num[]存储该集合中元素个数,并在集合合并时更新num[]即可。然后找出0所在的集合的根节点x,因此,num[x]就是answer了。
#include<cstdio>
using namespace std;
const int MAXN=30001;
int father[MAXN];
int num[MAXN];
void make_set(int x){
    father[x]=x;
    num[x]=1;
}
int find_set(int x){
    if(x!=father[x])
        father[x]=find_set(father[x]);
    return father[x];
}
void union_set(int x,int y){
    x=find_set(x);
    y=find_set(y);
    if(x==y)
        return ;
    father[y]=x;
    num[x]+=num[y];

}
int main(){
    int n,m,t,x,y;
    while(scanf("%d%d",&n,&m)){
        if(m==0&&n==0)
            break;
        if(m==0){
             printf("1\n");
             continue;
        }
        for(int i=0;i<n;i++)
            make_set(i);
        for(int i=0;i<m;i++){
            scanf("%d",&t);
            scanf("%d",&x);
            for(int j=1;j<t;j++){
                scanf("%d",&y);
                union_set(x,y);
            }
        }
        x=find_set(0);
        printf("%d\n",num[x]);
    }
}

 

POJ 1703 Find them, Catch them



题意 :有两个不同的帮派,每个帮派至少有一个人。 判断两个人是否属于同一个帮派。
              有 T 组测试数据。
              给你 N 个人,编号从 1 到 N,操作 M 次。
              每次操作输入一个字符和两个数 x ,y 
              如果字符为 A 则判断 x 和 y 是否属于同一个帮派,并且输出结果。
              如果字符为 D 则明确告诉你 x 和 y 是属于不同帮派的。
详细分析:

#include<cstdio>

const int maxn = 100000+10;

int p[maxn]; //存父亲节点
int r[maxn]; //存与根节点的关系,0 代表同类, 1代表不同类

int find(int x) //找根节点
{
    if(x == p[x]) return x;

    int t = p[x]; //记录父亲节点 方便下面更新r[]
    p[x] = find(p[x]);
    r[x] = (r[x]+r[t])%2; //根据子节点与父亲节点的关系和父节点与爷爷节点的关系,推导子节点与爷爷节点的关系
    return p[x]; //容易忘记
}

void Union(int x, int y)
{
    int fx = find(x); //x所在集合的根节点
    int fy = find(y);

    p[fx] = fy; //合并
    r[fx] = (r[x]+1+r[y])%2; //fx与x关系 + x与y的关系 + y与fy的关系 = fx与fy的关系
}
void set(int n)
{
    for(int x = 1; x <= n; x++)
    {
        p[x] = x; //自己是自己的父节点
        r[x] = 0; //自己和自己属于同一类
    }
}

int main()
{
    int T;
    int n, m;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d%*c", &n, &m);
        set(n);

        char c;
        int x, y;
        while(m--)
        {
            scanf("%c%d%d%*c", &c, &x, &y); //注意输入
            //printf("%c\n", c);
            if(c == 'A')
            {
                if(find(x) == find(y)) //如果根节点相同,则表示能判断关系
                {
                    if(r[x] != r[y]) printf("In different gangs.\n");
                    else printf("In the same gang.\n");
                }
                else printf("Not sure yet.\n");
            }
            else if(c == 'D')
            {
                Union(x, y);
            }
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值