并查集

来源:互联网 发布:好弹幕软件下载 编辑:程序博客网 时间:2024/05/21 10:08

并查集作用:将众多元素按条件构造成一个集合,举个不好的例子,1,2,3,4,5都是数字而abc是字母,所以把他们分成2个集合,比较好理解的例子是百度上那个:

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚

并查集的基础操作有:①将每个元素都当成一个集合,此时每个元素的根节点都是自身②查找每个集合的根节点【代码里的find()】③合并相同根节点的集合

以下从图的角度理解,图中每个字母代表一个元素,箭头指向的为根节点【引用CYJB博客园图片】

初始化,每个元素(节点)指向自己:



查找:查找过程能起到路径压缩作用,见图,如果不压缩,那么要找d的根节点需要先找到c,再找到c的父节点...,路径压缩使多个节点直接指向根节点,而不需经过父节点(即父节点就是根节点)



合并:将两个根节点不同的集合合并,即让其中一个集合成为另一个集合原先根节点的新根节点


//以上图片来自网上博客

例题:杭电1213 How Many Tables

题意:某人要办生日,来的朋友不想和不认识的人坐同一张桌子,给定朋友数和朋友的认识关系,问至少需要多少桌子(A认识B,B认识C,那么ABC就是相互认识了)

#include<cstdio>#define MAX 1002int f[MAX];int find(int x){if( x == f[x] ) return x;return f[x] = find(f[x]);}void merge(int x,int y){x = find(x);y = find(y);if(x != y)f[x] = y; //将y的集合合并到x的集合}int main(){int i,n,m,t,a,b,res;//freopen("a.txt","r",stdin);while( ~scanf("%d",&t) ){while(t--){res = 0;scanf("%d%d",&n,&m);for(i=1;i<MAX;i++)//初始化假定每个人只认识自己  f[i] = i;while(m--){scanf("%d%d",&a,&b);merge(a,b);//输入ab,他们认识,合并在一同一集合  }for(i=1;i<=n;i++){ //判断有多少个集合就是有多少群人相互认识,就有多少桌子 if( f[i] == i ) res++;//判断集合数目是根据根节点指向自己  }printf("%d\n",res);}}return 0;}

这种递归形式的路径压缩find()有个缺点:数据大(如10W)时可能栈溢出,于是有了非递归的路径压缩find()

int find(int x){    if( x == f[x] ) return x;    int rt=x;    while(rt!=f[rt])      //找到根结点        rt=f[rt];    int fx;    while(x!=rt)    {        fx=f[x];        f[x]=rt;        x=fx;    }return x;}


当我们希望集合不仅仅表示元素间是不是同属集合,还需要表示它们的关系时,就需要另开数组

POJ 2492 A Bug's Life

输入a,b表示ab异性,问是不是会造成矛盾,设r[]=0表示同性,r[]=1表示异性。

#include<stdio.h>const int maxn=2015;int f[maxn],r[maxn];void init(int n){    for(int i=1;i<=n;i++)    {        f[i]=i;        r[i]=0;    }}int find(int x){    if(x==f[x]) return x;    int fx=f[x];    f[x]=find(f[x]);    r[x]=(r[fx]+r[x])%2;    return f[x];}void merge(int x,int y){    int fx=find(x);    int fy=find(y);    if(fx!=fy)    {        f[fy]=fx;        r[fy]=(r[x]+r[y]+1)%2;    }}#define sc(x) scanf("%d",&x);int main(){//     freopen("1.in","r",stdin);    int t;    sc(t)    for(int ce=1;ce<=t;ce++)    {        int n,m;        sc(n);sc(m);        init(n);        int no=0;        while(m--)        {            int x,y;            sc(x)   sc(y)            if(no)  continue;            if(find(x)==find(y))            {//同一集合时(r[x]+r[y])%2为0就与输入的关系1矛盾                if(r[x]==r[y])                    no=1;            }            else//不同集合的合并肯定不会有问题                merge(x,y);        }        if(no)            printf("Scenario #%d:\nSuspicious bugs found!\n\n",ce);        else            printf("Scenario #%d:\nNo suspicious bugs found!\n\n",ce);    }    return 0;}

POJ 1182 食物链

#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <set>#include <map>#include <stack>#include <queue>#include <vector>#include <iostream>#include <algorithm>using namespace std;#define ll long long#define eps 10^(-6)#define Q_CIN ios::sync_with_stdio(false);#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )#define CLR( a , x ) memset ( a , x , sizeof (a) );#define RE freopen("1.in","r",stdin);#define WE freopen("1.out","w",stdout);#define MOD 10009#define debug(x) cout<<#x<<":"<<(x)<<emdl;const int maxn=50010;int f[maxn],r[maxn];//r=0:同类,r=1:被父亲吃,r=2:吃父亲int find(int x){    if(f[x]==x) return x;    int fx=f[x];        //原父亲(fx)    f[x]=find(f[x]);    //ffx    r[x]=(r[x]+r[fx])%3;    return f[x];}void init(int n){    for(int i=1;i<=n;i++)    {        f[i]=i;        r[i]=0;    }}int merge(int x,int y,int d){    int fx=find(x);    int fy=find(y);    f[fy]=fx;       //被 x 吃,所以以 x 的根为父    r[fy]=(3-r[y] + d-1 + r[x])%3;}#define sc(x) scanf("%d",&x);int main(){//    RE    int n,k,x,y,t,d;    sc(n) sc(k)    {        int cnt=0;        init(n);        while(k--)        {            sc(d) sc(x) sc(y)            if(x>n||y>n)    cnt++;            else if(d==2&&x==y)  cnt++;            else if(find(x)==find(y))            {                if(d==1 && r[x]!=r[y])cnt++;    //不同类                if(d==2 && (r[x]+1)%3!=r[y])  cnt++;            }            else                merge(x,y,d);        }        printf("%d\n",cnt);    }return 0;}



0 0
原创粉丝点击