ACM zoj 1789(并查集实现)

转载本博客上原创文章者,请注明出处。

这是道并查集的题目,刚开始我是用一个类来实现的。但很意外的是总是出现段错误(主要是指针乱指或者数组越界,但我发现不了这道题的问题),本来刚开始类的指针数据成员parent是指向动态申请的内存的,不行后改为一般的数组还是不行,最后还是将其改为非类来实现,结果AC了。(因此可能是ACM中最好不要用类实现的问题吧。)


程序如下:

#include <iostream>
#include <stdio.h>
#include <memory.h>

using namespace std;

int parent[30001];

//查找i所在的集合的元首,并对该树形结构进行优化
int find(int r)
{
	for(;parent[r]!=r;r=parent[r]); //直到它的根是其本身

	return r;
}


//将输入的组成员和其他相关联人员合并为一个集合
void weightedUnion(int* arr,int num)
{
	int root=find(arr[0]); //找到arr[0]所在集合的根

	for(int index=1;index<num;++index)
	{
		int r=find(arr[index]); //找到arr[index]所在的集合的根
		//如果集合的根不为root,则将其根置为root
		if(r!=root)
			parent[r]=root;
	}

}


//输出跟0在一组的人数,n表示人的总数
int countSick(int i,int n)
{
	int root=find(i); //找到这个结点的根

	int count=1; //这个只有自己这个结点时朋友数为1
	//每个非根结点都是以它所在树的树根为parent
	//从0结点的下一个结点开始算起
	for(int i=1;i<n;++i)
	{
		int r=find(i);

		if(r==root)
			++count;
	}

	return count;
}


int main()
{
	int N=0,M=0;
	int arr[30001];

	while(scanf("%d%d",&N,&M)!=EOF&&!(N==0&&M==0))
	{
		for(int i=0;i<N;++i)
			parent[i]=i;

		for(int i=0;i<M;++i)
		{
			int memNum=0;
			scanf("%d",&memNum);

			memset(arr,0,sizeof(arr)); //将数组元素都置为0

			for(int k=0;k<memNum;++k)
				scanf("%d",&arr[k]);

			weightedUnion(arr,memNum); //将成员并为一组
		}

		printf("%d\n",countSick(0,N)); //输出跟0在一组的成员数(0包括在内)
	}
}


通过这道题我基本了解了并查集的基本操作,就是先查找出两个集合各自的集合首元素,再将它们合为同一个集合。

在查找集合的首元素的find函数中,我们也可以做先优化,如每次查找到一个元素后,找到其所在集合的首元素,再将这个元素到首元素的所有元素的parent都置为这个首元素。

//查找i所在的集合的元首,并对该树形结构进行优化
int collaspingFind(int i)
{
	int r=i;

	for(;parent[r]>=0;r=parent[r]);

	//将i到集合首部元素的parent都保存集合的首元素r
	while(i!=r)
	{
		int s=parent[i];
		parent[i]=r;
		i=s;
	}

	return r;
}







   

你可能感兴趣的:(ACM,ACM,ZOJ,ZOJ,并查集)