pku1611

嘿嘿,第一道并查集的题目,一个基本的应用,求一个集合的元素个数,不过中间同样涉及了俩个基本的操作,查找还有合并

题目大意:有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;
}
posted @ 2011-05-03 11:37  枕边梦  阅读(261)  评论(0编辑  收藏  举报