【并查集专题】

来源:互联网 发布:剑三插件数据库 编辑:程序博客网 时间:2024/05/18 00:50

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 10001using 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

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

题目描述:

有很多组学生,在同一个组的学生经常会接触,也会有新的同学的加入。但是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


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


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

#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;}



0 0
原创粉丝点击