ACM zoj 1789(并查集实现)

来源:互联网 发布:扭力单位转换软件 编辑:程序博客网 时间:2024/06/04 19:39

原文链接:http://blog.csdn.net/iqrocket/article/details/8134569


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

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


程序如下:

[cpp] view plaincopy
  1. #include <iostream>  
  2. #include <stdio.h>  
  3. #include <memory.h>  
  4.   
  5. using namespace std;  
  6.   
  7. int parent[30001];  
  8.   
  9. //查找i所在的集合的元首,并对该树形结构进行优化  
  10. int find(int r)  
  11. {  
  12.     for(;parent[r]!=r;r=parent[r]); //直到它的根是其本身  
  13.   
  14.     return r;  
  15. }  
  16.   
  17.   
  18. //将输入的组成员和其他相关联人员合并为一个集合  
  19. void weightedUnion(int* arr,int num)  
  20. {  
  21.     int root=find(arr[0]); //找到arr[0]所在集合的根  
  22.   
  23.     for(int index=1;index<num;++index)  
  24.     {  
  25.         int r=find(arr[index]); //找到arr[index]所在的集合的根  
  26.         //如果集合的根不为root,则将其根置为root  
  27.         if(r!=root)  
  28.             parent[r]=root;  
  29.     }  
  30.   
  31. }  
  32.   
  33.   
  34. //输出跟0在一组的人数,n表示人的总数  
  35. int countSick(int i,int n)  
  36. {  
  37.     int root=find(i); //找到这个结点的根  
  38.   
  39.     int count=1; //这个只有自己这个结点时朋友数为1  
  40.     //每个非根结点都是以它所在树的树根为parent  
  41.     //从0结点的下一个结点开始算起  
  42.     for(int i=1;i<n;++i)  
  43.     {  
  44.         int r=find(i);  
  45.   
  46.         if(r==root)  
  47.             ++count;  
  48.     }  
  49.   
  50.     return count;  
  51. }  
  52.   
  53.   
  54. int main()  
  55. {  
  56.     int N=0,M=0;  
  57.     int arr[30001];  
  58.   
  59.     while(scanf("%d%d",&N,&M)!=EOF&&!(N==0&&M==0))  
  60.     {  
  61.         for(int i=0;i<N;++i)  
  62.             parent[i]=i;  
  63.   
  64.         for(int i=0;i<M;++i)  
  65.         {  
  66.             int memNum=0;  
  67.             scanf("%d",&memNum);  
  68.   
  69.             memset(arr,0,sizeof(arr)); //将数组元素都置为0  
  70.   
  71.             for(int k=0;k<memNum;++k)  
  72.                 scanf("%d",&arr[k]);  
  73.   
  74.             weightedUnion(arr,memNum); //将成员并为一组  
  75.         }  
  76.   
  77.         printf("%d\n",countSick(0,N)); //输出跟0在一组的成员数(0包括在内)  
  78.     }  
  79. }  


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

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

[cpp] view plaincopy
  1. //查找i所在的集合的元首,并对该树形结构进行优化  
  2. int collaspingFind(int i)  
  3. {  
  4.     int r=i;  
  5.   
  6.     for(;parent[r]>=0;r=parent[r]);  
  7.   
  8.     //将i到集合首部元素的parent都保存集合的首元素r  
  9.     while(i!=r)  
  10.     {  
  11.         int s=parent[i];  
  12.         parent[i]=r;  
  13.         i=s;  
  14.     }  
  15.   
  16.     return r;  
  17. }  
0 0
原创粉丝点击