并查集是一个非常巧妙的数据结构,将一系列输入的数据划分为多个不同的部分,从而进行相应的后续处理。
自己在学习并查集之前也实现过这种类型,不过没有进行压缩,时间复杂度很大。
对于并查集的讲解,试着自己给自己讲一遍,觉得自己的理解还是挺清楚的,但要写博文的话实在太费时间了,暂时放弃。
推荐大家看并查集讲解,作者的代码写的非常清楚,讲解也很有趣。
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1232
类似的还有:http://acm.hdu.edu.cn/showproblem.php?pid=1213
值得一提的是,最后必须用mark[find(i)而不是mark[pre[i]],因为并查集有时候不会被压缩。比如:1 2,2 3,对比1 2,2 3,1 4.
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000;
int mark[maxn];
int pre[maxn];
int find(int x)
{
int r=x;
while(r!=pre[r])
r=pre[r];
int i=x,j;
// while(i!=r)
while(pre[i]!=r)
{
j=pre[i];
pre[i]=r;
i=j;
}
return r; //r作为最终的节点
}
void mix(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx!=fy) pre[fx]=fy;//pre[fx]=fy也可以。但下面这个语句很显然是错的:fx=pre[fy],太容易疏忽的一个错了
}
int main(void)
{
int n,m;
int a,b;
int i,j,k;
int T;
while(cin>>n)
{
if(n==0) break;
for(i=1;i<=n;i++)
pre[i]=i;
cin>>m;
for(i=0;i<m;i++)
{
cin>>a>>b;
mix(a,b);
}
//如果一共有n块,那么需要n-1条道路
memset(mark,0,sizeof(mark));
for(i=1;i<=n;i++) //题目中输入的是测试数据,1-n,自己一开始写的是从0开始,所以
//测试数据过了但还是WA
mark[find(i)]=1;
int cnt=0;
for(i=1;i<=n;i++)
if(mark[i]) cnt++;
cout<<cnt-1<<endl;
// for(i=0;i<n;i++) cout<<mark[i]<<" ";
}
}