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