并查集

  • 概述
    并查集是一种数据结构,主要处理一些不相交的集合的合并问题。就是集合的合并操作。经典的例子有:连通子图、最小生成树Kruskal算法和最近公共祖先等。并查集主要操作有初始化、合并、查询。

  • 优化
    合并优化:在合并两个不同集合的元素时,需要找到他们的根结点,将根结点合并。在合并的时候将高度较小的树合并在高度较高的树下,能有效减小树的高度,有利于查询。

    路径压缩:在直接进行简单合并的情况下,在某个集合中会形成树状的结构,这种结构不利于根结点的查询(需要逐层向上查询,速度比较慢),所以在许多情况下需要在查询根结点的同时,将同一集合中的各元素的双亲直接更改为根结点,这样在查询的时候就会快很多,这个操作称为路径压缩。

    并不是在所有情况下都需要进行这两种优化,例如接下来龙珠的的题目中,合并后树的高度可以作为龙珠搬运的次数,在时间允许的范围内可以简化代码,思路也比较好理解。

  • 练习
    How Many Tables
    Ubiquitous Religions
    The Suspects
    Find them, Catch them
    Wireless Network
    A Bug’s Life
    Cube Stacking
    食物链
    Dragon Balls
    More is better
    小希的迷宫
    Is It A Tree?
    Farm Irrigation

    题意:n个人参加晚宴,完全不认识的两个人不能被分配在同一餐桌,认识具有传递性:A认识B B认识C,那么A和C也认识,所以A、B、C可以在同一张餐桌上。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    using namespace std;
    
    int pe[1050];
    int high[1050];//树的高度
    int N, M;
    int ans;
    
    void init_set()
    {
        for(int i=1; i<=N; i++)
        {
            pe[i] = i;
            high[i] = 0;
        }
    }
    
    int find_set(int x)
    {
        int root = x;
        while(root!= pe[root]) root = pe[root];
    
        int i = x, temp;
        while(pe[i]!=i)
        {
            temp = pe[i];
            pe[i] = root;
            i = temp;
        }
        return root;
    }
    
    void union_set(int A, int B)
    {
        A = find_set(A);
        B = find_set(B);
    
        if(high[A] == high[B])
        {
                pe[B] = A;
            high[A]++;
        }
    
        if(high[A]>high[B]) pe[B] = A;
        else pe[A] = B;
    }
    
    int main()
    {
        int T;
        cin>> T;
        while(T--)
        {
            scanf("%d%d", &N, &M);
            init_set();
    
            while(M--)
            {
                int A, B;
                scanf("%d%d", &A, &B);
                union_set(A, B);
            }
    
            ans = 0;
            for(int i=1; i<=N; i++)
            {
                if(pe[i] == i) ans++;
            }
    
            printf("%d\n", ans);
        }
    }
    

    题意:你的学校有n名学生(0 < n <= 50000),你不太可能询问每个人的宗教信仰,因为他们不太愿意透露。但是当你同时找到2名学生,他们却愿意告诉你他们是否信仰同一宗教,你可以通过很多这样的询问估算学校里的宗教数目的上限。你可以认为每名学生只会信仰最多一种宗教。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    using namespace std;
    
    int pe[50010];
    int high[50010];
    int N, M;
    int ans;
    
    void init_set()
    {
        for(int i=1; i<=N; i++)
        {
            pe[i] = i;
            high[i] = 0;
        }
    }
    
    int find_set(int x)
    {
        int root = x;
        while(root!= pe[root]) root = pe[root];
    
        int i = x, temp;
        while(pe[i]!=i)
        {
            temp = pe[i];
            pe[i] = root;
            i = temp;
        }
        return root;
    }
    
    void union_set(int A, int B)
    {
        A = find_set(A);
        B = find_set(B);
    
        if(high[A] == high[B])
        {
                pe[B] = A;
            high[A]++;
        }
    
        if(high[A]>high[B]) pe[B] = A;
        else pe[A] = B;
    }
    
    int main()
    {
        int c=1;
        while(scanf("%d%d", &N, &M))
        {
            if(N==0 && M==0) break;
            init_set();
    
            while(M--)
            {
                int A, B;
                scanf("%d%d", &A, &B);
                union_set(A, B);
            }
    
            ans = 0;
            for(int i=1; i<=N; i++)
            {
                if(pe[i] == i) ans++;
            }
    
            printf("Case %d: %d\n", (c++), ans);
        }
    }
    

    题意:有n个学生,m个社团。(0<n<=3000)(0<=m<=500),学生学号从0到n-1。已知0号学生已感染病毒。每个学生可能参加多个社团。
    输入:第一行,两个整数n,m;(当n=m=0时,结束),接下来的m行输入一个数k(这个社团的人数),后跟学生的编号。
    输出:对于每一个例子,输出一个数代表可能感染的总人数,占一行。
    明显是有和零号有接触的人都有事,只要将有联系的人分入同一组即可。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    using namespace std;
    
    int pe[50010];
    int N, M;
    int ans;
    
    void init_set()
    {
        for(int i=0; i<=N; i++)
        {
            pe[i] = i;
        }
    }
    
    int find_set(int x)
    {
        int root = x;
        while(root!= pe[root]) root = pe[root];
    
        int i = x, temp;
        while(pe[i]!=i)
        {
            temp = pe[i];
            pe[i] = root;
            i = temp;
        }
        return root;
    }
    
    void union_set(int A, int B)
    {
        A = find_set(A);
        B = find_set(B);
    
        if(B==0) pe[A] = B;
        else pe[B] = A;
    }
    
    int main()
    {
        while(scanf("%d%d", &N, &M))
        {
            if(N==0 && M==0) break;
            init_set();
    
            while(M--)
            {
                int k, first, another;
                scanf("%d%d", &k,&first);
    
                for(int i=1; i<k; i++)
                {
                    scanf("%d", &another);
                    union_set(first, another);
                }
            }
    
            ans = 0;
            int r = find_set(0);
            for(int i=0; i<=N; i++)
            {
                if(r == find_set(i)) ans++;
            }
    
            printf("%d\n", ans);
        }
    }
    

    题意:第一行表示用例组数,第二行表示人数和信息组数有两个帮派,D a b表示a和b属于不同帮派,A a b表示要你回答a和b是属于同一帮派、不同帮派还是并不确定。

    思路1:N个人分属于两个帮派。所以我们可以初始化一个N*2的并查集,然后给定D a b就unit(a,b+n),unit(b,a+n)。

    思路2:给的是a,b的敌对关系,可以用数组enemy把a的敌人存起来,然后如果a,x是敌人,把s[a]和x用并查集合并,再更新s[a]=x;

    #include<iostream>
    #include<cstdio>
    #include<string.h>
    using namespace std;
    #define MAX 100010
    
    int high[MAX];
    int s[MAX], enemy[MAX];
    int t, n, m;
    char c;
    int a, b;
    
    void init_set()
    {
        for(int i=0; i<=n+1; i++)
        {
    //        enemy[i] = 0;
            s[i] = i;
            high[i] = 0;
        }
    }
    
    int find_set(int x)
    {
        int root = x;
        while(root!= s[root]) root = s[root];
    
        int i = x, temp;
        while(s[i]!=i)
        {
            temp = s[i];
            s[i] = root;
            i = temp;
        }
        return root;
    }
    
    void union_set(int A, int B)
    {
        A = find_set(A);
        B = find_set(B);
    
        if(high[A] == high[B])
        {
            s[B] = A;
            high[A]++;
        }
    
        if(high[A]>high[B]) s[B] = A;
        else s[A] = B;
    }
    
    int main()
    {
        scanf("%d", &t);
    
        while(t--)
        {
            scanf("%d%d", &n, &m);
            init_set();
            memset(enemy, 0, sizeof(enemy));
    
            while(m--)
            {
                getchar();
                scanf("%c%d%d", &c, &a, &b);
    
                if(c=='D')
                {
                    if(enemy[a])
                    {
                        union_set(enemy[a], b);
                    }
                    if(enemy[b])
                    {
                        union_set(enemy[b], a);
                    }
                    enemy[a] = b;
                    enemy[b] = a;
                }
    
                else
                {
                    if(find_set(a)==find_set(b))
                    {
                        printf("In the same gang.\n");
                    }
                    else if(find_set(a)==find_set(enemy[b]))
                    {
                        printf("In different gangs.\n");
                    }
                    else
                    {
                        printf("Not sure yet.\n");
                    }
                }
            }
        }
    
        return 0;
    }
    

    题意:给你n个电脑的位置,和信号覆盖的的半径d,一开始所有的电脑都坏了,你每单位时间可以进行 O(修复一台电脑),S(检查两台电脑是否联通,间接联通也算),只有修理好的计算机才能连通,如果两台计算机的距离不超过d,则两台电脑之间可以直接连接。

    #include<iostream>
    #include<cstdio>
    #include<string.h>
    using namespace std;
    #define MAX 1010
    
    typedef struct
    {
        int x, y;
    }computer;
    
    bool fcp[MAX];
    computer cp[MAX];
    int high[MAX];
    int s[MAX];
    int n, d, dd;
    int p, q;
    char c;
    
    void init_set()
    {
        for(int i=0; i<=n+1; i++)
        {
            s[i] = i;
            high[i] = 0;
        }
    }
    
    int find_set(int x)
    {
        int root = x;
        while(root!= s[root]) root = s[root];
    
        int i = x, temp;
        while(s[i]!=i)
        {
            temp = s[i];
            s[i] = root;
            i = temp;
        }
        return root;
    }
    
    void union_set(int A, int B)
    {
        A = find_set(A);
        B = find_set(B);
    
        if(high[A] == high[B])
        {
            s[B] = A;
            high[A]++;
        }
    
        if(high[A]>high[B]) s[B] = A;
        else s[A] = B;
    }
    
    int main()
    {
        memset(fcp, false, sizeof(fcp));
        scanf("%d%d", &n, &d);
        dd = d*d;
    
        init_set();
    
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d", &cp[i].x, &cp[i].y);
        }
    
        getchar();
        while((scanf("%c", &c))!=EOF)
        {
            if(c=='S')
            {
                scanf("%d%d", &p, &q);
    
                if((find_set(p)==find_set(q))) printf("SUCCESS\n");
                else printf("FAIL\n");
            }
            else
            {
                bool flag=true;
                scanf("%d", &p);
                for(int i=1; i<=n; i++)
                {
                    if(fcp[i] && i!=p)
                    {
                        int xx = (cp[p].x-cp[i].x)*(cp[p].x-cp[i].x);
                        int yy = (cp[p].y-cp[i].y)*(cp[p].y-cp[i].y);
                        if((xx+yy)<=dd)
                        {
                            union_set(i, p);
                        }
                    }
                }
                fcp[p]=true;
            }
            getchar();
        }
        return 0;
    }
    

    题意:t组数据,n个虫子,m组相互喜爱的关系,虫子分为雌雄两种,每个虫子只有一个性别,问是否存在同性恋的虫子。
    Find them, Catch them思路相似。

    #include<iostream>
    #include<cstdio>
    #include<string.h>
    using namespace std;
    #define MAX 2010
    
    int high[MAX];
    int s[MAX], enemy[MAX];
    int t, n, m;
    bool flag;
    int a, b;
    
    void init_set()
    {
        for(int i=0; i<=n+1; i++)
        {
            s[i] = i;
            high[i] = 0;
        }
    }
    
    int find_set(int x)
    {
        int root = x;
        while(root!= s[root]) root = s[root];
    
        int i = x, temp;
        while(s[i]!=i)
        {
            temp = s[i];
            s[i] = root;
            i = temp;
        }
        return root;
    }
    
    void union_set(int A, int B)
    {
        A = find_set(A);
        B = find_set(B);
    
        if(high[A] == high[B])
        {
            s[B] = A;
            high[A]++;
        }
    
        if(high[A]>high[B]) s[B] = A;
        else s[A] = B;
    }
    
    int main()
    {
        scanf("%d", &t);
    
        for(int i=1; i<=t; i++)
        {
            scanf("%d%d", &n, &m);
            init_set();
            memset(enemy, 0, sizeof(enemy));
            flag = true;
    
            while(m--)
            {
                scanf("%d%d", &a, &b);
    
                if(flag)
                {
                    if(find_set(a)==find_set(b))
                    {
                        flag = false;
                    }
                    else
                    {
                        if(enemy[a])
                        {
                            union_set(enemy[a], b);
                        }
                        if(enemy[b])
                        {
                            union_set(enemy[b], a);
                        }
                        enemy[a] = b;
                        enemy[b] = a;
                    }
                }
            }
    
            printf("Scenario #%d:\n", i);
            if(flag) printf("No suspicious bugs found!\n\n");
            else printf("Suspicious bugs found!\n\n");
        }
    
        return 0;
    }
    

    题意:有n个从1到n编号的箱子,将每个箱子当做一个栈,对这些箱子进行p次操作,每次操作分别为以下两种之一:
    1.输入 M x y:表示将编号为x的箱子所在的栈放在编号为y的箱子所在栈的栈顶.
    2.输入 C x:计算编号为x的所表示的栈中在x号箱子下面的箱子数目.

    带权并查集。(待完善)

    #include<iostream>
    #include<cstdio>
    #include<string.h>
    using namespace std;
    #define MAX 30010
    
    int s[MAX], val[MAX], dis[MAX];
    int t;
    char c;
    int a, b;
    
    void init_set()
    {
        for(int i=0; i<MAX; i++)
        {
            dis[i] = 0;
            s[i] = i;
            val[i] = 1;
        }
    }
    
    int find_set(int x)
    {
        if(x!=s[x])
        {
            int temp = s[x];
            s[x] = find_set(s[x]);
            dis[x] += dis[temp];
        }
        return s[x];
    }
    
    void union_set(int A, int B)
    {
        A = find_set(A);
        B = find_set(B);
    
        if(A!=B)
        {
            s[B] = A;
            dis[B] = val[A];
            val[A] += val[B];
        }
    }
    
    int main()
    {
        scanf("%d", &t);
        init_set();
    
        while(t--)
        {
    
            getchar();
            scanf("%c", &c);
    
            if(c=='M')
            {
                scanf("%d%d", &a, &b);
                union_set(a, b);
            }
            else
            {
                scanf("%d", &a);
                int x=find_set(a);
                printf("%d\n", val[x]-dis[a]-1);
            }
        }
    
        return 0;
    }
    

    题意:动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。有人用两种说法对这N个动物所构成的食物链关系进行描述:
    第一种说法是"1 X Y",表示X和Y是同类。
    第二种说法是"2 X Y",表示X吃Y。
    此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
    1) 当前的话与前面的某些真的话冲突,就是假话;
    2) 当前的话中X或Y比N大,就是假话;
    3) 当前的话表示X吃X,就是假话。
    你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
    Find them, Catch them相似,用的思路1的思想,开三倍大的数组。参考

    #include<iostream>
    #include<cstdio>
    #include<string.h>
    using namespace std;
    #define MAX 50010
    
    int s[3*MAX];  //x<-x+n,x+n<-x+2*n,x+2*n<-x
    int n, k, d;
    int x, y;
    int ans;
    
    void init_set()
    {
        for(int i=1; i<3*MAX; i++)
        {
            s[i] = i;
        }
    }
    
    int find_set(int x)
    {
        int root = x;
        while(root!= s[root]) root = s[root];
    
        int i = x, temp;
        while(s[i]!=i)
        {
            temp = s[i];
            s[i] = root;
            i = temp;
        }
        return root;
    }
    
    void union_set(int A, int B)
    {
        A = find_set(A);
        B = find_set(B);
    
        s[A] = B;
    }
    
    int main()
    {
        scanf("%d%d", &n, &k);
        init_set();
    
        while(k--)
        {
            scanf("%d%d%d", &d, &x, &y);
    
            if(x>n || y>n)
            {
                ans++;
                continue ;
            }
    
            if(d==1)
            {
                if(find_set(x+2*n)==find_set(y) || find_set(x)==find_set(y+2*n)) ans++;
                else
                {
                    union_set(x, y);
                    union_set(x+n, y+n);
                    union_set(x+2*n, y+2*n);
                }
            }
            else
            {
                if(x==y || find_set(x)==find_set(y+2*n) || find_set(x)==find_set(y)) ans++;
                else
                {
                    union_set(x+2*n, y);
                    union_set(x+n, y+2*n);
                    union_set(x, y+n);
                }
    
            }
        }
    
        printf("%d", ans);
        return 0;
    }
    

    题意:先说每个城市和龙珠都有编号,对应的第i个龙珠放在第i个城市。T A B表示把A号龙珠所在的城市的所有龙珠全部搬运到B号龙珠所在的城市。
    Q A表示要求出X(第A号龙珠所在的城市编号),Y(第X号城市存放的龙珠个数),Z(第A号龙珠被搬运的次数)。

    带权并查集。但是可以知道,龙珠每搬运一次,在并查集中的深度就会加一(未优化),所以在不优化并查集的前提下,龙珠的深度就是搬运次数。

    #include<iostream>
    #include<cstdio>
    #include<string.h>
    using namespace std;
    #define MAX 10010
    
    //int trans[MAX];
    int s[MAX], sum[MAX];
    int ans[10010][3];
    int t;
    int n, q;
    int a, b;
    int deep;
    
    void init_set()
    {
        for(int i=1; i<=n; i++)
        {
            s[i] = i;
            sum[i] = 1;
    //        trans[i] = 0;
        }
    }
    
    int find_set(int x)
    {
        int root = x;
        while(root!= s[root])
        {
            root = s[root];
            deep++;
        }
        return root;
    }
    
    void union_set(int A, int B)
    {
        A = find_set(A);
        B = find_set(B);
    
        if(A!=B)
        {
            s[A] = B;
            sum[B] += sum[A];
            sum[A] = 0;
        }
    
    }
    
    int main()
    {
        scanf("%d", &t);
    
        for(int i=1; i<=t; i++)
        {
            printf("Case %d:\n", i);
    
            scanf("%d%d", &n, &q);
            init_set();
    
            while(q--)
            {
                char c;
                getchar();
                scanf("%c", &c);
    
                if(c=='T')
                {
                    scanf("%d%d", &a, &b);
    
                    union_set(a, b);
                }
                else
                {
                    scanf("%d", &a);
                    deep = 0;
                    b = find_set(a);
                    printf("%d %d %d\n", b, sum[b], deep);
                }
            }
        }
        return 0;
    }
    

    题意:要找一些人完成一项工程。要求最后挑选出的人之间都是朋友关系,可以说直接的,也可以是间接地。问最多可以挑选出几个人(最少挑一个)。
    在基础的并查集上加个数组记录集合的数量。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 10000005
    
    int sum[MAX];
    int s[MAX];
    int ans;
    int n;
    int a, b;
    
    void init_set()
    {
        for(int i=1; i<MAX; i++)
        {
            s[i] = i;
            sum[i] = 1;
        }
    }
    
    int find_set(int x)
    {
        int root = x;
        while(root!= s[root]) root = s[root];
    
        int i = x, temp;
        while(s[i]!=i)
        {
            temp = s[i];
            s[i] = root;
            i = temp;
        }
        return root;
    }
    
    void union_set(int A, int B)
    {
        A = find_set(A);
        B = find_set(B);
        if(A!=B)
        {
            s[B] = A;
            sum[A] += sum[B];
            if(sum[A]>ans) ans = sum[A];
        }
    }
    
    int main()
    {
    
        while((scanf("%d", &n))!=EOF)
        {
            init_set();
            if(n==0)
            {
                printf("1\n");
                continue;
            }
    
            ans=0;
            while(n--)
            {
                scanf("%d%d", &a, &b);
                union_set(a, b);
            }
    
            printf("%d\n", ans);
        }
        return 0;
    }
    

    思路:每输入一组数据,都要对其进行连接,如果两个点find(a)==find(b),那么说明他们已经是一个集合的了,如果再连接a,b两个点,就会构成回路,这里也就是要输出no.
    坑点:给出的图不一定是连通图,可能有多个连通分量。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<string.h>
    using namespace std;
    #define MAX 100010
    
    bool vis[MAX];
    int s[MAX];
    int a, b;
    bool flag;
    char c;
    
    void init()
    {
        for(int i=0; i<MAX; i++)
        {
            s[i] = i;
            vis[i] = false;
        }
    }
    
    int find_set(int x)
    {
        int root=x;
        while(root!=s[root]) root = s[root];
    
        int i = x, temp;
        while(s[i]!=i)
        {
            temp = s[i];
            s[i] = root;
            i = temp;
        }
        return root;
    }
    
    void union_set(int a, int b)
    {
        a = find_set(a);
        b = find_set(b);
    
        if(a==b) flag = false;
        else s[a] = b;
    
        vis[a] = true;
        vis[b] = true;
    }
    
    int main()
    {
        while(~scanf("%d%d", &a, &b))
        {
            flag = true;
            if(a==-1 && b==-1) break;
    
            if(a==0 && b==0)
            {
                printf("Yes\n");
                continue;
            }
    
            if(a==0 || b==0) flag = false;
            init();
    
            union_set(a, b);
    
            while(scanf("%d%d", &a, &b)&&a&&b)
            {
                if(!flag) continue;
                else union_set(a, b);
            }
    
            int r=0;
            if(flag)
            {
                for(int i=1; i<MAX; i++)
                {
                    if(vis[i] && find_set(i)==i)
                    {
                        r++;
                    }
                }
            }
            
            if(flag && r==1) printf("Yes\n");
            else printf("No\n");
        }
        return 0;
    }
    

    题意:根据给出的数据判断是否能构成一棵树。特判0 0 是一棵空树。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<string.h>
    using namespace std;
    #define MAX 1000000
    
    bool vis[MAX];
    int mx;
    int s[MAX];
    int a, b;
    bool flag;
    char c;
    
    void init()
    {
        flag = true;
        for(int i=0; i<MAX; i++)
        {
            s[i] = i;
            vis[i] = false;
        }
    }
    
    int find_set(int x)
    {
        int root=x;
        while(root!=s[root]) root = s[root];
    
        int i = x, temp;
        while(s[i]!=i)
        {
            temp = s[i];
            s[i] = root;
            i = temp;
        }
        return root;
    }
    
    void union_set(int a, int b)
    {
        vis[a] = true;
        vis[b] = true;
    
        int aa = find_set(a);
        int bb = find_set(b);
    
        if(aa==bb || bb!=b) flag=false;
        else s[bb] = aa;
    }
    
    int main()
    {
        for(int i=1; ; i++)
        {
            scanf("%d%d", &a, &b);
    
            if(a<0 && b<0) break;
    
            if(a==0 && b==0)
            {
                printf("Case %d is a tree.\n", i);
                continue;
            }
    
            init();
            union_set(a, b);
    
            while(scanf("%d%d", &a, &b)&&a&&b)
            {
                if(!flag) continue;
                else union_set(a, b);
            }
    
            int root = 0;
            if(flag)
            {
                for(int i=0; i<MAX; i++)
                {
                    if(vis[i] && find_set(i)==i)
                    {
                        root++;
                    }
                }
            }
    
            if(flag && root==1) printf("Case %d is a tree.\n", i);
            else printf("Case %d is not a tree.\n", i);
        }
        return 0;
    }
    
    题意:给你不同形状 的水管,看哪些水管聚类连通,分成 N 堆答案就是 N。 更容易想到DFS
    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    using namespace std;
    #define MAX 55
    
    typedef struct
    {
        int u, d, l, r;
    }square;
    
    int m ,n;
    char c;
    int ans;
    int farm[MAX][MAX];
    square squares[11]={{1,0,1,0}, {1,0,0,1}, {0,1,1,0}, {0,1,0,1}, {1,1,0,0}, {0,0,1,1}, {1,0,1,1}, {1,1,1,0}, {0,1,1,1}, {1,1,0,1}, {1,1,1,1}};
    int dir[4][2]={{-1, 0}, {0, -1}, {1, 0}, {0, 1}};  //上左下右
    
    
    bool check(int i, int j)
    {
        if(i<0 || i>=m || j<0 || j>=n) return false;
        else return true;
    }
    
    void DFS(int i, int j)
    {
        int ii, jj, x, p;
        p = farm[i][j];
        farm[i][j] = -1;
    
        ii = i+dir[0][0];
        jj = j+dir[0][1];
        if(check(ii, jj) && farm[ii][jj]!=-1)
        {
            x = squares[p].u + squares[farm[ii][jj]].d;
            if(x == 2) DFS(ii, jj);
        }
    
        ii = i+dir[1][0];
        jj = j+dir[1][1];
        if(check(ii, jj) && farm[ii][jj]!=-1)
        {
            x = squares[p].l + squares[farm[ii][jj]].r;
            if(x == 2) DFS(ii, jj);
        }
    
        ii = i+dir[2][0];
        jj = j+dir[2][1];
        if(check(ii, jj) && farm[ii][jj]!=-1)
        {
            x = squares[p].d + squares[farm[ii][jj]].u;
            if(x == 2) DFS(ii, jj);
        }
    
        ii = i+dir[3][0];
        jj = j+dir[3][1];
        if(check(ii, jj) && farm[ii][jj]!=-1)
        {
            x = squares[p].r + squares[farm[ii][jj]].l;
            if(x == 2) DFS(ii, jj);
        }
    }
    
    
    int main()
    {
        while(scanf("%d%d", &m, &n))
        {
            if(m==-1 && n==-1) break;
            if(m==0 && n==0)
            {
                printf("0\n");
                continue;
            }
    
            ans = 0;
            for(int i=0; i<m; i++)
            {
                string s;
                cin>> s;
                for(int j=0; j<n; j++) farm[i][j] = s[j]-'A';
            }
    
            for(int i=0; i<m; i++)
            {
                for(int j=0; j<n; j++)
                {
                    if(farm[i][j]!=-1)
                    {
                        ans++;
                        DFS(i, j);
                    }
                }
            }
    
            printf("%d\n", ans);
        }
    }
    
发布了9 篇原创文章 · 获赞 1 · 访问量 1349
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章