转载本博客上原创文章者,请注明出处。
这是道并查集的题目,刚开始我是用一个类来实现的。但很意外的是总是出现段错误(主要是指针乱指或者数组越界,但我发现不了这道题的问题),本来刚开始类的指针数据成员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;
}
836

被折叠的 条评论
为什么被折叠?



