http://noi.openjudge.cn/_4.3算法之图论_1526:宗教信仰

题目

1526:宗教信仰
总时间限制: 5000ms 内存限制: 65536kB
描述
世界上有许多宗教,你感兴趣的是你学校里的同学信仰多少种宗教。
你的学校有n名学生(0 < n <= 50000),你不太可能询问每个人的宗教信仰,因为他们不太愿意透露。但是当你同时找到2名学生,他们却愿意告诉你他们是否信仰同一宗教,你可以通过很多这样的询问估算学校里的宗教数目的上限。你可以认为每名学生只会信仰最多一种宗教。

输入
输入包括多组数据。
每组数据的第一行包括n和m,0 <= m <= n(n-1)/2,其后m行每行包括两个数字i和j,表示学生i和学生j信仰同一宗教,学生被标号为1至n。输入以一行 n = m = 0 作为结束。
输出
对于每组数据,先输出它的编号(从1开始),接着输出学生信仰的不同宗教的数目上限。
样例输入
10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0
样例输出
Case 1: 1
Case 2: 7

代码

#include
using namespace std;
int f[50000],//信仰什么宗教的学生群体的代表是谁
s[50000];//该群体的人数
int n,//几个学生
m,//几对学生的宗教信仰一样
a,b,//一对学生
ans;//学生共信仰几个宗教
int fa(int x){//选出学生代表
if(f[x]= =x)return x;//递归出口,该学生的代表是自己
else{
return f[x]=fa(f[x]);//递归调用 fa 函数找到代表,并将 f[x] 更新为父节点,实现路径压缩。
}
}
void merge(int a,int b){//两个学生找到代表
int af=fa(a),//找到第一学生的代表
bf=fa(b);//找到第二学生的代表
if(af==bf)return;//一样就没必要再找代表了
else{//根据 s[af] 和 s[bf] 的大小,将代表同学较小的集合合并到较多的集合中
ans–;//两学生信仰一样,所以不一样的宗教少一个
if(s[af] else f[bf]=af,s[af]+=s[bf];
}
}
void view(string sx){
cout< for(int i=1;i<=n;i++)cout< cout< }
int main(){
//freopen(“data.cpp”,“r”,stdin);
int x=1;
while(cin>>n>>m&&n&&m){
ans=n;
for(int i=1;i<=n;i++)f[i]=i,s[i]=1;//开始,学生代表自己
for(int i=1;i<=m;i++){
cin>>a>>b;
merge(a,b);//两学生信仰一样,选共同代表
}
//view(“前”);
for(int i=1;i<=n;i++)f[i]=fa(i);//统一共同体的唯一代表
//view(“后”);
cout<<"Case “<

}
return 0;

}

数据和结果

http://noi.openjudge.cn/_4.3算法之图论_1526:宗教信仰_第1张图片
http://noi.openjudge.cn/_4.3算法之图论_1526:宗教信仰_第2张图片

并查集(ai讲解)

查找操作(Find):查找元素 x 所属集合的代表元素。通过递归或迭代的方式,不断查找元素的父节点,直到找到代表元素(即 parent[x] == x)。为了优化查找操作,可以使用路径压缩,将查找路径上的元素直接指向代表元素,减少后续查找的时间复杂度。
合并操作(Union):将两个集合合并。首先找到两个元素所属集合的代表元素,然后将其中一个代表元素的父节点设置为另一个代表元素,从而将两个集合合并为一个集合。为了保持树的平衡性,可以使用按秩合并的方法,记录每个集合的秩(高度或大小),将秩较小的集合合并到秩较大的集合中。

小结

并查集就是查找和合并,可我总觉着什么内容没弄清除。
//view(“前”);
for(int i=1;i<=n;i++)f[i]=fa(i);//统一共同体的唯一代表
//view(“后”);
代码后补充了上行后明朗多了。也许这就是答案。

你可能感兴趣的:(并查集,递归)