1. 问题描述:
有一个叫做“数码世界”奇异空间,在数码世界里生活着许许多多的数码宝贝,其中有些数码宝贝之间可能是好朋友,并且数码宝贝世界有两条不成文的规定:
第一,数码宝贝A和数码宝贝B是好朋友等价于数码宝贝B与数码宝贝A是好朋友
第二,如果数码宝贝A和数码宝贝C是好朋友,而数码宝贝B和数码宝贝C也是好朋友,那么A和B也是好朋友,现在给出这些数码宝贝中所有好朋友的信息,问:可以把这些数码宝贝分成多少组,满足每组中的任意两个数码宝贝都是好朋友,而且任意两组之间的数码宝贝都不是好朋友
2. 思路分析:
① 分析题目可以知道这个是一个并查集问题的模型:将所有具有之间或者是间接联系的节点连接在一起(分组),题目中的好朋友关系我们可以将其理解为两个顶点之间存在边的关系,我们在输入这些好朋友关系的时候就可以对其进行并查集的合并操作,这样处理完了之后我们就可以得到一个若干个的集合
② 因为要求解的是集合的数目,那么我们可以声明一个bool数组来记录根节点的数目,初始化的时候每一个节点都不是根节点,我们在进行并查集的合并操作之后查询出每个顶点父节点,然后将父节点的位置标记为true
③ 在并查集查找的过程中使用的是递归查找的方法,查找的过程中对节点之间的关系进行了合并
④ 最后统计出bool数组中根节点的数目,这个就是我们最终需要求解的集合的数目
3. 代码如下:
#include<cstdio>
const int N = 110;
int father[N];
bool isRoot[N];
int findFather(int x){
if(x == father[x]) return x;
int fa = findFather(father[x]);
father[x] = fa;
return fa;
}
//并查集的合并操作
void Union(int x, int y){
int fax = findFather(x);
int fay = findFather(y);
if(fax != fay)
//两者的根节点不一样才进行合并操作
father[fax] = fay;
}
void init(int n){
for(int i = 1; i <= n; i++){
father[i] = i;
isRoot[i] = false;
}
}
int main(void){
int n, m, a, b;
scanf("%d%d", &n, &m);
init(n);
for(int i = 0; i < m; ++i){
scanf("%d%d", &a, &b);
Union(a, b);
}
// 下面这一步其实是再一次合并各个节点的根节点
for(int i = 0; i < n; ++i){
isRoot[findFather(i + 1)] = true;
}
int ans = 0;
for(int i = 0; i < n; ++i){
ans += isRoot[i + 1];
}
printf("%d\n", ans);
}