题目描述:
http://poj.org/problem?id=1611
中文大意:
SARS 是一种传播性极强的感染病,为了最大程度地控制其传播,最好的策略是将嫌疑人与其他人分开。
在大学中,有很多学生团体,一个学生可以加入多个团体。
规定:一旦团体中的某个成员成为可疑对象,则该团体中的所有成员都将成为可疑对象。
要求:统计可疑对象的数量。
思路:
一个学生团体就是一个集合。
我们可以让团体内所有同学,都与第一个同学 x 合并。
若某位同学 y 还属于别的集合(pre[y] != y),则将这两个集合合并。
最终,同学 0 所在集合的节点数即为可疑对象数量。
代码:
#include<iostream>
using namespace std;
int n,m;
int pre[30000];//各节点的所属集
int height[30000];//各集合的高度
//初始化,每个节点属于独立的集合
void init(){
for(int i=0;i<n;i++){
pre[i] = i;
height[i] = 1;
}
}
//查询节点 x 的所属集
int find(int x){
//寻找根节点
int root = x;
while(root != pre[root]){
root = pre[root];
}
//路径压缩:将路径上节点的所属集修改为根节点
int i = x;
while(pre[i] != root){
int j = pre[i];
pre[i] = root;
i = j;
}
return root;
}
//合并
void union_set(int x, int y){
//寻找节点 x 的所属集
x = find(x);
//寻找节点 y 的所属集
y = find(y);
//若两个集合的树高相等,则将 x 的所属集并到 y 的所属集
if(height[x] == height[y]){
pre[x] = y;
height[y]++;
}//否则,将矮树并到高树,并且高树的高度保持不变
else if(height[x] > height[y]){
pre[y] = x;
}
else{
pre[x] = y;
}
}
int main(){
while(scanf("%d %d", &n, &m) && (n||m)){
init();
int k, x, y;
for(int i=0;i<m;i++){
scanf("%d %d", &k, &x);
//k 个数属于同一个集合,用第一个数与其他数字进行合并
for(int j=1;j<k;j++){
scanf("%d", &y);
union_set(x, y);
}
}
//统计 0 所在集合的节点数
int root0 = find(0);
int num = 1;
for(int i=1;i<n;i++){
int root = find(i);
if(root == root0){
num++;
}
}
printf("%d
", num);
}
}