c++實現島問題(並查集)

1. 問題描述

一個矩陣中只有0和1兩種值,每個位置都可以和自己的上、下、左、右四個位置相連,如果有一片1連在一起,這個部分叫做一個島,求一個矩陣中有多少個島?

比如:
0 0 1 0 1 0
1 1 1 0 1 0
1 0 0 1 0 0
0 0 0 0 0 0

這個矩陣中有三個島。

2. 思路

我們的主體是進行二維數組的遍歷,同時我們也定義一個感染函數。比如我們遇到一個1時候,我們對這個點的上下作用進行感染,讓其賦值爲2,所以會導致一片爲1的點都會被感染爲2,只統計第一個1,然後進行計數就會得到所有的島嶼數目。

3. 代碼

#include <iostream>
#include <vector>

void infectLands(std::vector<std::vector<int>>& lands, int N, int M, int n, int m) {
    if (n < 0 || n >=N || m < 0 || m >= M || lands[n][m] != 1) {
        return;
    }
    lands[n][m] = 2;
    infectLands(lands, N, M, n + 1, m);
    infectLands(lands, N, M, n - 1, m);
    infectLands(lands, N, M, n, m + 1);
    infectLands(lands, N, M, n, m - 1);
}

int countIslands(std::vector<std::vector<int>>& lands) {
    if (lands.size() == 0 || lands[0].size() == 0) {
        return 0;
    }
    int res = 0;
    int N = lands.size();
    int M = lands[0].size();
    for (int i = 0; i < N; ++i) {
        for (int j = 0; j < M; ++j) {
            if (lands[i][j] == 1) {
                res ++;
                infectLands(lands, N, M, i, j);
            }

        }
    }
    return res;
}

int main()
{
    std::vector<std::vector<int>> lands =  {{0, 0, 1, 0, 1, 0},
                                        {1, 1, 1, 0, 1, 0},
                                        {1, 0, 0, 1, 0, 0},
                                        {0, 0, 0, 0, 0, 0}};
    std::cout << countIslands(lands) << std::endl;
    return 0;
}

在這裏插入圖片描述

4. 進一步擴展

現在我們對島問題進行擴展,加入我們現在這個島很大,那麼有沒有什麼方法快速求解了。

方法:一種直觀的思想就是使用分治的思想,也就是我們可以先劃分,然後進行合併。但是現在有一個問題,因爲每一塊邊界上也有可能連成一片,這樣我們在求解的時候還是很複雜的。針對邊界,我們藉助並查集這種方法。

5. 並查集簡單介紹

參考文獻:

  1. 並查集詳解 ——圖文解說,簡單易懂(轉)
  2. ACM-並查集之暢通工程——hdu1232

一般介紹並查集都是先引入暢通工程這個例子。

5.1 暢通工程

Problem Description

某省調查城鎮交通狀況,得到現有城鎮道路統計表,表中列出了每條道路直接連通的城鎮。省政府“暢通工程”的目標是使全省任何兩個城鎮間都可以實現交通(但不一定有直接的道路相連,只要互相間接通過道路可達即可)。問最少還需要建設多少條道路?

Input

測試輸入包含若干測試用例。每個測試用例的第1行給出兩個正整數,分別是城鎮數目N ( < 1000 )和道路數目M;隨後的M行對應M條道路,每行給出一對正整數,分別是該條道路直接連通的兩個城鎮的編號。爲簡單起見,城鎮從1到N編號。
注意:兩個城市之間可以有多條道路相通,也就是說
3 3
1 2
1 2
2 1
這種輸入也是合法的
當N爲0時,輸入結束,該用例不被處理。

Output

對每個測試用例,在1行裏輸出最少還需要建設的道路數目。

Sample Input

4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0

Sample Output

1
0
2
998

Hint

Huge input, scanf is recommended.

5.2 並查集求解暢通工程

#include <iostream>

const int MAX = 1000;
int father[MAX];

void initUnionSet(int n) {
    for (int i = 1; i <= n; ++i) {
        father[i] = i;
    }
}

int findUnionSet(int x) {
    int root = x;
    while (root != father[root]) {
        root = father[root];
    } // get the root
    int node = x;
    while (node != root) {
        int temp = father[node];    // save the temp node
        father[node] = root;   // compress path
        node = temp;
    }
    return root;    // return the root
}

void combine(int unionSet1, int unionSet2) {
    int root1 = findUnionSet(unionSet1);
    int root2 = findUnionSet(unionSet2);
    if (root1 != root2) {
        father[root1] = root2;
    }
}

int findAns(int n) {
    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        if (father[i] == i) {
            ++sum;
        }
    }
    return sum;
}

int main() {
    int m, n, a, b;
    while (std::cin>>n) {
        if (int(n) == 0) break;
        initUnionSet(n);
        std::cin >> m;
        for (int i = 1; i <= m; ++i) {
            std::cin >> a >> b;
            combine(a, b);
        }
        std::cout << "outpus: " << findAns(n) - 1 << std::endl;
    }

    return 0;
}

在這裏插入圖片描述
這裏對並查集代碼就不做太多的解釋了,有興趣的同學可以看看上面的參考文獻。

6. 並查集求解島問題

有了二部分的代碼,我們就可以利用並查集的方法進一步加速島問題的求解了。代碼這裏自己沒有實現,感興趣的同學可以自己探索探索。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章