题目描述:
http://acm.hdu.edu.cn/showproblem.php?pid=1213
中文大意:
朋友聚会,要求将直接或间接认识的人安排在一个桌子上,问最少需要几张桌子。
思路:
一个桌子就是一个集合。若 x y 为朋友关系,则将二者所在的集合合并。
数组 pre[] 记录的是节点的所属集,根节点的所属集 pre[x] = x;
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int pre[1001];//各节点的所属集
int height[1001];//各集合的树高,用来做优化
//初始化,每个节点属于独立的集合
void init(){
for(int i=1;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]){
height[y]++;
pre[x] = y;
}//否则,矮树并到高树上,并且高树的树高保持不变
else if(height[x] > height[y]){
pre[y] = x;
}
else{
pre[y] = x;
}
}
int main(){
int t;
scanf("%d", &t);
while(t--){
scanf("%d %d", &n, &m);
init();
int x,y;
for(int i=0;i<m;i++){
scanf("%d %d", &x, &y);
union_set(x, y);
}
//统计集合数量
int num = 0;
for(int i=1;i<=n;i++){
if(pre[i] == i){
num++;
}
}
printf("%d
", num);
}
}