POJ.1611 The Suspects (并查集)

 POJ.1611 The Suspects (并查集)

题意分析

对于每组测试数据:
这个学校有n个人,m小组,依次给出m个小组的信息,每行一个小组。
每个小组的信息包括,k,表示有k个人,k个数字,分别是这k个人的ID。
现在ID为0的人感染了病毒,他及他的小组成员全部会感染病毒,每个小组成员都会感染自己所在小组的成员(每个成员可以加入多个小组)。问现在总共有多少个人感染了病毒?
比较裸的并查集。
我们可以把每个人看作是一个集合,对于一个小组的成员,就是不停地在做并集操作。并集的时候,同时要合并他们下面的节点个数。最后只要找到0的根是谁,输出其下的节点个数即可。

代码总览

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <sstream>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#define INF 0x3f3f3f3f
#define nmax 30005
#define MEM(x) memset(x,0,sizeof(x))
using namespace std;
int father[nmax];
int rnk[nmax];//秩
int num[nmax];//爸爸下节点的个数,集合中元素个数
void makeset(int x)
{
    father[x] = x;//自己是自己的爸爸
    rnk[x] = 0;//秩为0
    num[x] = 1;//爸爸下面元素个数为1
}
int findset(int x)
{
    int r = x,temp;
    while(father[r] != r) r = father[r];// 一直向上找
    //压缩路径
    while(r != x){
        temp = father[x];//暂存爸爸
        father[x] = r;//直接把爸爸改成根
        x = temp;//向上
    }
    return r;
}
void unionset(int x, int y)
{
    x = findset(x);
    y = findset(y);
    if(x == y) return;// 根相同 不用合并
    if(rnk[x] > rnk[y]){
        father[y] = x; // 小的并到大的上
        num[x]+=num[y];
    }else{
        father[x] = y;
        if(rnk[x] == rnk[y])
            rnk[x]++;
        num[y]+=num[x];
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    int n,m;
    while(scanf("%d %d",&n,&m)){
        if(n == 0 && m == 0) break;
        if(m == 0){
            printf("1\n");
            continue;
        }
        for(int i = 0; i<n;++i) makeset(i);//每个人自己是一个set
        for(int j = 0; j<m;++j){
            int num,first,temp; scanf("%d",&num);
            for(int k = 0; k<num;k++){
                if(!k) scanf("%d",&first);
                else{
                    scanf("%d",&temp);
                    unionset(first,temp);
                }
            }
        }
        int rt = findset(0);
        printf("%d\n",num[rt]);
    }
    return 0;
}
posted @ 2017-05-01 19:00  pengwill  阅读(93)  评论(0编辑  收藏  举报