int pre[N]; //代表x的上级 int zxy(int x) //x要去找他的老大,看看他和另y是不是同一个帮派 { int r = x; //让r去找x的上级 while (pre[r] != r) //如果他的上级不是他自己 r = pre[r]; //就令r的上级去找他的上级,以此类推 int i = x, j; while (i != r) //如果r的上级不是老大 { j = pre[i]; //这时来了个j说:我来帮r的上级来管理下属,r的上级你去找你的上级吧 pre[i] = r; //但是r却说:j大哥,不用你在这帮忙, //我跟我的上级(pre[r])说了,他先让我暂时帮他管理一下,就不用麻烦您了。 //这里就相当于r直接升了一个等级,占了他上级(pre[r])的位置。 //以此类推,就相当于r直接去找老大了(仔细想想对不对?)。 i = j; //j这时说:不过你的下属没人管了啊,我去帮你管管吧,你就去找老大吧。 } return r; //老大现身(登登登----登---------) } int zhoier(int x , int y) //让x和y成为一个帮派的 { int fx = zxy(x) , fy = zxy(y); //x和y通过zxy这个强大的天神终于找到了他们的老大 if(fx != fy) //如果x和y的老大不是一个人 pre[fx] = fy; //因为x的老大比较淡泊名利,所以就让y的老大当帮主,所以x的老大的上级就成了y的老大 //帮派合并完成!!! }
并查集就是用来解决合并集合的,只要能灵活运用以上两种操作,那么对于并查集的基本几个问题就可以得出解了。
下面给一道例题:hdu-1232
并查集的思想在这道题中是十分明显的,对于每一条道路所连接的两个城市,每组两个城市各进行子集判断,只要稍微改一下zhoier函数就行,也是一道比较水的并查集的题:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> int pre[1000]; int find(int x) { return x == pre[x] ? x : x=find(pre[x]); } int main() { int n,m,p1,p2,i,total,f1,f2; while(scanf("%d",&n) && n) //读入n,如果n为0,结束 { //刚开始的时候,有n个城镇,一条路都没有 //那么要修n-1条路才能把它们连起来 total=n-1; //每个点互相独立,自成一个集合,从1编号到n //所以每个点的上级都是自己 for(i=1;i<=n;i++)
{ pre[i]=i; } //共有m条路 scanf("%d",&m); while(m--) { //下面这段代码,其实就是zhoier函数,只是稍作改动以适应题目要求 //每读入一条路,看它的端点p1,p2是否已经在一个连通分支里了 scanf("%d %d",&p1,&p2); f1=find(p1); f2=find(p2); //如果是不连通的,那么把这两个分支连起来 //分支的总数就减少了1,还需建的路也就减了1 if(f1!=f2) { pre[f2]=f1; total--; } //如果两点已经连通了,那么这条路只是在图上增加了一个环
//对连通性没有任何影响,无视掉 } //最后输出还要修的路条数 printf("%d ",total); } return 0; }