zoukankan      html  css  js  c++  java
  • 并查集

    作用:

      并查集顾名思义就是有“合并集合”和“查找集合中的元素”两种操作的关于数据结构。

    代表元思想:

      用集合中的某个元素来代表这个集合,该元素称为集合的代表元
    组织结构:

      一个集合内的所有元素组织成以代表元为根的树形结构。也就是是说同一个集合中的元素在同一棵树里。

    合并两个集合:

      将一棵树作为另一棵树的子树。也就是将代表元作为别人的子节点。

    查找集合中的两个元素:

      讨论他们代表元是否一样。

    一些题目:

    POJ1703  Find them, Catch them

    题意:

    有两个帮派,

    操作D:表示 a,b不属于一个帮派 。操作A:询问a,b是否为同一个帮派。

    思路:

    同一个代表元中的东西表示他们属于同一集合。我们可以很容易表示a,b属于同一个集合,那么如何表示属于不同的集合。

    我们可以用a+N表示a的对立面,如果a+N和b在同一集合内,那么表示a,b不在同一个集合内。

    代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 const int MAXN=1e5+7;
     4 int par[MAXN*2];
     5 int find(int x){
     6     return par[x]==x?par[x]:par[x]=find(par[x]);
     7 }
     8 void unit(int a,int b){
     9     a=find(a);
    10     b=find(b);
    11     if(a!=b){
    12         par[a]=b;
    13     }
    14 }
    15 int main(){
    16     int T;scanf("%d",&T);
    17     while(T--){
    18         int N,M;scanf("%d%d",&N,&M);getchar();
    19         for(int i=1;i<=N*2;++i){
    20             par[i]=i;
    21         }
    22         for(int i=0;i<M;++i){
    23             char ch=getchar();int a,b;
    24             scanf("%d%d",&a,&b);getchar();
    25             if(ch=='D'){
    26                 unit(a,b+N);
    27                 unit(a+N,b);
    28             }else{
    29                 int x1=find(a),x2=find(a+N);
    30                 int y1=find(b),y2=find(b+N);
    31                 if(x1==y1||x2==y2)printf("In the same gang.
    ");
    32                 else if(x1==y2||x2==y1)printf("In different gangs.
    ");
    33                 else printf("Not sure yet.
    ");
    34             }
    35         }
    36     }
    37     return 0;
    38 }
    poj1703

     也可以用带权并查集做:

    这里有篇讲得不错的博客:http://blog.csdn.net/mengxiang000000/article/details/51142345

    大概思想就是通过这样的思想:

    敌人的敌人是朋友。

    通过计算两个节点间的距离来确定这两个点是不是同一个集合里的。

    代码:

    #include<cstdio>
    #include<cstring>
    const int MAXN=1e5+7;
    int par[MAXN],sum[MAXN];
    int find(int x){
        if(par[x]==x)return x;
        int top=find(par[x]);
        sum[x]+=sum[par[x]];
        return par[x]=top;
    }
    int main(){
        int T;scanf("%d",&T);
        while(T--){
            int N,Q;scanf("%d%d",&N,&Q);getchar();
            for(int i=1;i<=N;++i)par[i]=i,sum[i]=0;
            for(int i=0;i<Q;++i){
                char ch;int x,y;
                scanf("%c%d%d",&ch,&x,&y);getchar();
                if(ch=='D'){
                    int xx=find(x);
                    int yy=find(y);
                    if(xx==yy)continue;
                    par[xx]=yy;
                    //printf("par[%d]=%d
    ",xx,par[xx]);
                    sum[xx]=sum[y]+sum[x]+1;
                    //printf("sum[%d]=%d
    ",xx,sum[xx]);
                }else{
                    int xx=find(x);
                    int yy=find(y);
                    if(yy==xx){
                        if((sum[x]-sum[y])%2==0){
                            printf("In the same gang.
    ");
                        }else{
                            printf("In different gangs.
    ");
                        }
                    }else{
                        if(N==2)printf("In different gangs.
    ");
                        else printf("Not sure yet.
    ");
                    }
                }
            }
        }
        return 0;
    }
    poj1703带权并查集

     ural 1003 parity

    题意:

        给你一些信息l,r,odd/even 代表从l到r的和为奇数或者偶数。问从哪一条开始错了。

    思路:

       考虑到数据范围很大,但N不大,可以考虑离散化。然后sum[l...r]=sum[r]-sum[l-1],也就是转化为前缀和。如果是偶数那么r和l-1在同一个集合里,否则就不是。我们认为在一个集合里的奇偶性是一样的。需要注意的是需要先判断是否符合条件,再进行集合的合并。顺序千万不要弄错!因为先进行合并会改变他们的关系。

       另外,对在不在一个集合里,奇数偶数这种转化为并查集思想一定要敏感。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<map>
    using namespace std;
    map<int,int>m;
    const int MAXN=1e4+7;
    int par[MAXN*2];
    int tot;
    int get(int x){
        if(!m[x])m[x]=tot++;
        return m[x];
    }
    int find(int x){
        return par[x]==x?par[x]:par[x]=find(par[x]);
    }
    void unit(int x,int y){
        //printf("unit:x=%d,y=%d
    ",x,y);
        x=find(x);
        y=find(y);
        if(x!=y){
            par[x]=y;
        }
    }
    int main(){
        int T;
        while(scanf("%d",&T)&&T!=-1){
            int N;scanf("%d",&N);
            for(int i=1;i<=N*2*2;++i)par[i]=i;
            m.clear();
            tot=1;
            int ans=N;
            for(int k=1;k<=N;++k){
                char s[10];
                int x,y;scanf("%d%d%s",&x,&y,s);
                if(ans!=N)continue;
                x--;
                x=get(x);y=get(y);
                if(s[0]=='e'){
                    int xx=find(x),yy=find(y);
                    if(find(x)==find(y+2*N)||find(x+2*N)==find(y)){
                        ans=k-1;
                    }else if(xx!=yy){
                        unit(x,y);
                        unit(x+2*N,y+2*N);
                    }
                }else{
                    int xx=find(x),yy=find(y);
                    if(find(x)==find(y)||find(x+2*N)==find(y+2*N)){
                        ans=k-1;
                    }else if(xx!=yy){
                        unit(x,y+2*N);
                        unit(x+2*N,y);
    
                    }
                }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    ural1003
  • 相关阅读:
    函数配接器
    函数对象和函数指针
    unary_function 和 binary_function
    堆排序
    Shell排序
    volatile理解
    死锁
    进程间通信
    优化循环的方法-循环展开
    如何学习编译原理
  • 原文地址:https://www.cnblogs.com/sun-yinkai/p/8093273.html
Copyright © 2011-2022 走看看