并查集基础~poj1611

http://poj.org/problem?id=1611

并查集的基本思想就是:它的每个元素开始都属于各自的一个集合,查找两个元素所属的集合是否属于同一个集合,如果不是则合并~   

    实现方法很简单~ 用树来实现~所有属于同一集合的元素属于同一棵树,

这样我们就可以用数根来表示一个集合,要找到某个元素属于哪个集合
,只要找到这个元素所在的树的树根;要合并两个集合,只要合并两棵树。一般比
较方便的方法是用父亲数组Parent[]来表示树,Parent[i]就是节点i的父结点,树
根的父结点就是它本身,关于并查集的优化,可以在find的时候进行路径压缩~只有两层

int find(int x)
{
 if(x!=p[x]) p[x]=find(p[x]);//每次查找的时候,都进行了压缩~
 return p[x];
}

下面附上代码:

#include <iostream>
using namespace std;
int n,m,k,t,f,p[30001],rank[30001],a,b;
int find(int x)
{
 if(x==p[x]) return x;
 else return p[x]=find(p[x]);
}
void un(int x,int y)
{
 a=find(x);
 b=find(y);
 if(a==b) 
  return;
 if(rank[a]>rank[b])
  p[b]=a;
 else
 {
  p[a]=b;
        if(rank[a]==rank[b])
   rank[b]++;
 }
}
int main()
{
 int i,sum;
 while(cin>>m>>n&&(m||n))
 {
  for(i=0;i<m;i++)
  {
   p[i]=i;
   rank[i]=0;
  }
  for(i=0;i<n;i++)
  {
   cin>>k;
   if(k>=1)
    cin>>f;
   for(int j=1;j<k;j++)
   {
    cin>>t;//读入、合并
  un(f,t);
   }
  }
     sum=1;
  for(i=1;i<m;i++)
  {
   if(find(i)==find(0))
    sum++;  
  }
  cout<<sum<<endl;
 }
 return 1;

}

这里的rank[i]标记这个i集合的深度

基本过程是:读入数据,判断~合并,最后找和0在同一个集合中的节点的个数~得解

poj2524跟此题类似

当然这都是并查集的基础题,下一道题是非常经典的~食物链

你可能感兴趣的:(并查集基础~poj1611)