zoukankan      html  css  js  c++  java
  • 并查集练兵场

    并查集: 

    N 个不同的元素分布在若干个互不相交集合
    中,需要进行以下3个操作:
     1. 合并两个集合
     2. 查询一个元素在哪个集合
     3. 查询两个元素是否属于同一集合

     一般用下面两个模板: 

    int get_par(int a)
    {
        if(par[a]!=a) par[a] = get_par(par[a]);
        return par[a];
    } 
    /* 也可以表示成这种形式,两种形式与数组的初始化有关。 
    int get_par(int a)
    {
        if(par[a]==-1) return a;
        return par[a] = get_par(par[a]);
    }
    */
    int query(int a, int b)
    {
        return get_par(a)==get_par(b);
    }
    
    void merge(int a, int b)
    {
        par[get_par(a)]=get_par(b);
    }

    《1》简单模板题。

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

    #include<iostream>
    using namespace std;
    
    const int MAXN = 30000;
    int n, m, k;
    int parent[MAXN+10];
    int total[MAXN+10];
    
    int GetParent(int a)
    {
        if(parent[a]!=a)
        parent[a]=GetParent(parent[a]);
        return parent[a];
    } 
    
    void Merge(int a, int b)
    {
        int p1 = GetParent(a);
        int p2 = GetParent(b);
        if(p1==p2)
        return;
        total[p1]+=total[p2];
        parent[p2] = p1;
    }
    
    int main()
    {
        while(true)
        {
            scanf("%d%d", &n, &m);
            if(n==0&&m==0) break;
            for(int i=0; i<n; i++)
            parent[i] = i, total[i]=1;
            for(int i=0; i<m; ++i)
            {
                int h,s;
                scanf("%d", &k);
                scanf("%d", &h);
                for(int j=1; j<k; j++)
                {
                    scanf("%d", &s);
                    Merge(h, s);
                }
            }
                printf("%d
    ", total[GetParent(0)]);
        }
            return 0;
    }
    View Code

     《2》再来道裸题。

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

    #include<iostream>
    #include<cstring>
    using namespace std;
    
    const int MAXN = 50000 + 10;
    int Fa[MAXN];
    
    int find(int x)
    {
        if(Fa[x]==-1) return x;
        return Fa[x]=find(Fa[x]);
    }
    
    void Merge(int u, int v)
    {
        int t1 = find(u);
        int t2 = find(v);
        if(t1!=t2) Fa[t1] = t2;
    }
    
    int main()
    {
        //freopen( "in.txt", "r", stdin );
        //freopen( "out.txt", "w", stdout );
        int n, m, u, v;
        int kase = 0;
        while(scanf("%d%d", &n, &m)==2)
        {
            if(n==0&&m==0) break;
            kase++;
            memset(Fa, -1, sizeof(Fa));
            while(m--)
            {
                scanf("%d%d", &u, &v);
                Merge(u, v);
            }
            int ans = 0;
            for(int i=1; i<=n; i++)
            if(Fa[i]==-1) ans++;
            
            printf("Case %d: %d
    ", kase, ans);
        }
        return 0;
    }
    View Code

     《3》 这个题对小恪来说有点难, 但是收获不小哦!

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

    这道题很容易想到暴力的方法, 很不幸的是一定会超时。  嘿嘿!

    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    const int MAXN = 30000+10;
    int parent[MAXN];
    int sum[MAXN]; //表示砖块 i 所在堆的砖块总数。
    int under[MAXN];//under[i]表示砖块 i 下面有多少砖块。
    
    int GetParent(int a)
    {
        if(parent[a]==a) return a;
        int t=GetParent(parent[a]);
        under[a]+=under[parent[a]];
        parent[a] = t;
        return parent[a];
    } 
    
    //把 b 所在的堆,叠放到 a 所在的堆。 
    void Merge(int a, int b)
    {
        int n;
        int pa = GetParent(a);
        int pb = GetParent(b);
        if(pa==pb) return;
        parent[pb] = pa;
        under[pb] = sum[pa];//under[pb]在赋值前一定是 0 ,
        //因为parent[pb] = pb, pb一定是最底下的。 
        sum[pa]+=sum[pb];
    }
    
    int main()
    {
        int p;
        for(int i=0; i<MAXN; i++)
        {
            sum[i] = 1;
            under[i] = 0;
            parent[i] = i;
        }
        scanf("%d", &p);
        for(int i=0; i<p; i++)
        {
            char s[20]; int a, b;
            scanf("%s", s);
            if(s[0] == 'M')
            {
                scanf("%d%d", &a, &b);
                Merge(b, a);
            }
            else
            {
                scanf("%d", &a);
                GetParent(a);
                printf("%d
    ", under[a]);
            }
        }
        return 0;
    }
    View Code

     《4》这个题可以用纯粹的并查集解, 但是如何判定合并条件有点虐心!

    http://acm.hdu.edu.cn/showproblem.php?pid=1198

    #include<cstdio>
    const int MAXN = 100 + 10;
    char map[MAXN][MAXN];
    int Fa[MAXN*MAXN];
    
    int find(int x)
    {
        if(Fa[x]==-1) return x;
        return Fa[x] = find(Fa[x]);
    }
    
    void Merge(int a, int b)
    {
        int t1 = find(a);
        int t2 = find(b);
        if(t1!=t2) Fa[t1] = t2;
    }
    
    int main()
    {
        int m, n;
        while(scanf("%d%d", &m, &n))
        {
            if(m<0||n<0) break;
            for(int i=0; i<m; i++)
            scanf("%s", &map[i]);
            for(int i=0; i<m*n; i++)
            Fa[i]= - 1;
            for(int i=0; i<m; i++)
            for(int j = 0; j<n; j++)
            {
                //判定条件有点虐心!   开口向: 上 下  左  右。 
                if(i>0&&(map[i][j]=='A'||map[i][j]=='B'||map[i][j]=='E'||map[i][j]=='G'||map[i][j]=='H'||map[i][j]=='J'||map[i][j]=='K'))
                if(map[i-1][j]=='C'||map[i-1][j]=='D'||map[i-1][j]=='E'||map[i-1][j]=='H'||map[i-1][j]=='I'||map[i-1][j]=='J'||map[i-1][j]=='K')
                Merge(i*n+j, (i-1)*n+j);
                if(j>0&&(map[i][j]=='A'||map[i][j]=='C'||map[i][j]=='F'||map[i][j]=='G'||map[i][j]=='H'||map[i][j]=='I'||map[i][j]=='K'))
                if(map[i][j-1]=='B'||map[i][j-1]=='D'||map[i][j-1]=='F'||map[i][j-1]=='G'||map[i][j-1]=='I'||map[i][j-1]=='J'||map[i][j-1]=='K')
                Merge(i*n+j,i*n+j-1);
            }
            int res = 0;
            for(int i=0; i<m*n; i++)
            if(Fa[i]==-1) res++;
            printf("%d
    ", res);
        }
        return 0;
    }
    View Code

     《5》赤裸裸的模板题

    http://acm.hdu.edu.cn/showproblem.php?pid=1232

    #include<cstdio>
    const int MAXN = 1000 + 10;
    int Fa[MAXN];
    
    int find(int x)
    {
        if(Fa[x]==-1) return x;
        return Fa[x] = find(Fa[x]);
    }
    
    void Merge(int a, int b)
    {
        int t1 = find(a);
        int t2 = find(b);
        if(t1!=t2) Fa[t1] = t2;
    }
    
    int main()
    {
        int n, m;
        while(scanf("%d%d", &n, &m)!=EOF, n)
        {
            for(int i=1; i<=n; i++)
            Fa[i] = -1;
            int a, b;
            while(m--)
            {
                scanf("%d%d", &a, &b);
                Merge(a, b);
            }
            int res = 0;
            for(int i=1; i<=n; i++)
            if(Fa[i]==-1) res++;
            printf("%d
    ", res-1);
        }
        return 0;
    }
    View Code

     《6》来道难的吧(欧拉回路+字典树+并查集)

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

    此题好坑! 本想巧妙地绕过字典序, 然而题目中并未告诉你一共有那几种颜色(感觉这一点好贱!), 结果此题机智的卡死了我这等弱渣,弱渣是何等的悲哀啊! 先贴一下我的wong代码(适合各种颜色首字母都不同的情况, 此题数据中应有首字母相同的情况)

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<string>
    #include<vector>
    using namespace std;
    
    const int maxn = 1000 + 5;
    
    int pa[256];
    int findset(int x) { return pa[x] != x ? pa[x] = findset(pa[x]) : x; } 
    
    int used[256], deg[256]; // 是否出现过;度数
    
    int main()
    {
        int flag = 0;
        freopen("in.txt", "r", stdin);
        freopen("out.txt", "w", stdout);
        char s1[105], s2[105];
        memset(used, 0, sizeof(used));
        memset(deg, 0, sizeof(deg));
        for(int ch = 'a'; ch <= 'z'; ch++) pa[ch] = ch; // 初始化并查集
    
        int cc = 26; // 连通块个数
    
        while(scanf("%s%s", s1, s2)!=EOF)
        {
            flag = 1;
            char c1 = s1[0], c2 = s2[0];
            deg[c1]++;
              deg[c2]--;
              used[c1] = used[c2] = 1;
              int s1 = findset(c1), s2 = findset(c2);
              if(s1 != s2) { pa[s1] = s2; cc--; }
        }
        vector<int> d;
        for(int ch = 'a'; ch <= 'z'; ch++) {
          if(!used[ch]) cc--; //没出现过的字母 
          else if(deg[ch] != 0) d.push_back(deg[ch]);
        }
        bool ok = false;
        if(cc == 1 && (d.empty() || (d.size() == 2 && (d[0] == 1 || d[0] == -1)))) ok = true;
        if(ok&&flag) printf("Possible
    ");
        else printf("Impossible
    ");
        return 0;
      }
      
    View Code

    下面是一巨巨的AC代码:

    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int MAXN=500010;
    int F[MAXN];
    const int MAX=26;
    int degree[MAXN];//度数
    int color;//颜色编号,从0开始,最后就是颜色总数
    int find(int x)
    {
        if(F[x]==-1)return x;
        return F[x]=find(F[x]);
    }
    void bing(int a,int b)
    {
        int t1=find(a);
        int t2=find(b);
        if(t1!=t2) F[t1]=t2;
    }
    typedef struct Trie_Node
    {
        bool isWord;
        struct Trie_Node *next[MAX];
        int id;
    }Trie;
    int insert(Trie *root,char *word)//返回颜色编号
    {
        Trie *p=root;
        int i=0;
        while(word[i]!='')
        {
            if(p->next[word[i]-'a']==NULL)
            {
                Trie *temp=new Trie;
                temp->isWord=false;
                for(int j=0;j<MAX;j++)
                  temp->next[j]=NULL;
                temp->isWord=false;
                temp->id=0;
                p->next[word[i]-'a']=temp;
            }
            p=p->next[word[i]-'a'];
            i++;
        }
        if(p->isWord)
        {
            return p->id;
        }
        else
        {
            p->isWord=true;
            p->id=color++;
            return p->id;
        }
    }
    void del(Trie *root)
    {
        for(int i=0;i<MAX;i++)
        {
            if(root->next[i]!=NULL)
             del(root->next[i]);
    
        }
        free(root);
    }
    int main()
    {
       // freopen("in.txt","r",stdin);
       // freopen("out.txt","w",stdout);
        char str1[20],str2[20];
        Trie *root=new Trie;
        for(int i=0;i<MAX;i++)
          root->next[i]=NULL;
        root->isWord=false;
        root->id=0;
        color=0;
        memset(F,-1,sizeof(F));
        memset(degree,0,sizeof(degree));
        while(scanf("%s%s",&str1,&str2)!=EOF)
        {
            int t1=insert(root,str1);
            int t2=insert(root,str2);
         //   printf("%d %d
    ",t1,t2);
            degree[t1]++;
            degree[t2]++;
            bing(t1,t2);
    
        }
    //***********************************************
    //这个是根据F数组等于-1来找根结点
        int cnt1=0,cnt2=0;
        for(int i=0;i<color;i++)
        {
            if(F[i]==-1)cnt1++;
            if(degree[i]%2==1)cnt2++;
            if(cnt1>1)break;
            if(cnt2>2)break;
        }
        //数据比较坑人,存在0根木棒的情况,此时cnt1==0
        if((cnt1==0||cnt1==1)&&(cnt2==0||cnt2==2))
          printf("Possible
    ");
        else printf("Impossible
    ");
        //del(root);//单组输入可以不释放空间,可以节省时间
        return 0;
    
    /*
        //********************************
        //这种是找寻根结点,多个根节点来判断是不是连通
        bool flag=true;
        int tt1=find(0);
        int cnt=0;//统计度数为奇数的结点个数
        for(int i=0;i<color;i++)
        {
            if(find(i)!=tt1)flag=false;//不连通也不是欧拉回路
            if(!flag)break;
            if(degree[i]%2==1) cnt++;
            if(cnt>2) flag=false;//度数为奇的结点个数>2,肯定不是欧拉回路了
        }
        if(cnt==1)flag=false;
        if(flag) printf("Possible
    ");
        else printf("Impossible
    ");
        del(root);//单组输入可以不释放空间,可以节省时间
        return 0;
        //******************************************
        */
    }
    View Code

    我还是   too   yang! 草滩小恪仍需恶补!!!

     《7》用并查集判断种类

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

    #include <stdio.h>
    #include <algorithm>
    #include <iostream>
    #include <string.h>
    using namespace std;
    
    const int MAXN=100010;
    int Fa[MAXN];
    int val[MAXN];
    int find(int x)
    {
        if(Fa[x]==-1)return x;
        int tmp=find(Fa[x]);
        val[x]=(val[x]+val[Fa[x]])%2;
        return Fa[x]=tmp;
    }
    void Merge(int x,int y)
    {
        int t1=find(x);
        int t2=find(y);
        if(t1!=t2)
        {
            Fa[t1]=t2;
            val[t1]=(val[y]-val[x]+1)%2;
        }
    }
    int main()
    {
        int T;
        char str[10];
        int u,v;
        int n,m;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            memset(Fa,-1,sizeof(Fa));
            memset(val,0,sizeof(val));
            while(m--)
            {
                scanf("%s%d%d",&str,&u,&v);
                if(str[0]=='A')
                {
                    if(find(u)!=find(v))
                    {//题目说两个集团至少有一个人,所以N==2的时候单独考虑,但是不考虑这个也可以AC,估计没有这样的数据
                        if(n==2)printf("In different ganges.
    ");
                        else printf("Not sure yet.
    ");
                    }
                    else
                    {
                        if(val[u]!=val[v])printf("In different gangs.
    ");
                        else printf("In the same gang.
    ");
                    }
                }
                else
                {
                    Merge(u,v);
                }
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    【Java EE 学习 36】【struts2】【struts2系统验证】【struts2 ognl值栈】【struts2 ongl标签】【struts2 UI标签】【struts2模型驱动和令牌机制】
    【Java EE 学习 35 下】【struts2】【struts2文件上传】【struts2自定义拦截器】【struts2手动验证】
    【Java EE 学习 35 上】【strus2】【类型转换器】【struts2和Servlet API解耦】【国际化问题】【资源文件乱码问题已经解决】
    【Java EE 学习 34】【struts2学习第一天】
    【JavaScript中的正则表达式】
    【Java EE 学习 33 下】【validate表单验证插件】
    【Java EE 学习 33 上】【JQuery样式操作】【JQuery中的Ajax操作】【JQuery中的XML操作】
    【Java EE 学习 32 下】【JQuery】【JQuey中的DOM操作】
    【Java EE 学习 32 上】【JQuery】【选择器】
    【Java EE 学习 31】【JavaScript基础增强】【Ajax基础】【Json基础】
  • 原文地址:https://www.cnblogs.com/acm1314/p/4596579.html
Copyright © 2011-2022 走看看