嘿嘿,第一道并查集的题目,一个基本的应用,求一个集合的元素个数,不过中间同样涉及了俩个基本的操作,查找还有合并
题目大意:有n个学生(标号为0 to n-1),m个学生社团,给出每个社团里所有学生的标号,并假设0号学生患有SARS(社团里只要用一个学生患病,则整个社团里的学生都会被隔离),问最后一共会有多少学生被隔离?
这是一个最基础的并查集的应用,扫描每一个社团,只要两个学生出现在同一个社团,则将这两个集合合并起来,最后输出0号点所在集合的rank值集合(rank值记录这个集合中的元素个数并用一个flag值跟踪0号元素所在集合标号)即可。
#include<stdio.h> #define MAXN 30010 int f[MAXN],r[MAXN],flag; //f[x]表示元素f的父节点 ////由于不知道应该将子树挂到那个集合上面去,故需要一个准则,这里的准则是将子树挂到 ////r值大的集合上面去,初始状态下r数组的值均为一,代表每个分支下只有一个数字 //flag用来追踪0号学生所属集合 int find(int x) { if(x==f[x]) return f[x]; f[x]=find(f[x]); return f[x]; }//路径压缩 void Union(int x,int y) { int a=find(x); int b=find(y); if(a==b) return ; if(r[a]<=r[b]) { f[a]=b; r[b]+=r[a]; if(a==flag) flag=b;//扩大0号学生的感染范围,b为0号学生所属集合的父节点 } else { f[b]=a; r[a]+=r[b]; if(b==flag) flag=a; } return ; } int main() { int i,j,n,m,temp1,temp2,num; while(scanf("%d %d",&n,&m)!=EOF &&(n||m)) { flag=0; for(i=0;i<n;i++)//有0号学生,所以这里i必须从0开始 { f[i]=i; r[i]=1; } for(i=1;i<=m;i++) { scanf("%d",&num); for(j=1;j<=num;j++) { if(j==1) { scanf("%d",&temp1); } else { scanf("%d",&temp2); Union(temp1,temp2); } } } printf("%d\n",r[flag]);//输出集合中的元素个数 } return 0; }