並查集

  • 概述
    並查集是一種數據結構,主要處理一些不相交的集合的合併問題。就是集合的合併操作。經典的例子有:連通子圖、最小生成樹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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章