1. 程式人生 > 其它 >洛谷P5877 棋盤遊戲

洛谷P5877 棋盤遊戲

洛谷P5877 棋盤遊戲 題解

前言

蒟蒻來水題解啦

Description

給定一個 \(N * N\) 的矩陣中的 \(M\) 個點,求這些點中的連通塊數量。

Solution

正常人看到這道題的第一反應應該是廣搜吧。但是,很明顯使用 BFS 會超時:我們需要求 \(M\) 次連通塊的數量。

那麼怎麼辦呢?涼拌蒟蒻! 我們可以使用 二維並查集 進行統計。

為什麼我們可以使用並查集呢?我們可以把一個連通塊想象成一個樹,新加進來(或與其相鄰的)的節點都是它的子節點。那麼,這棵樹的根就是這個連通塊在並查集裡的祖先。

那還剩下一個問題。二維並查集這麼搞?

其實我們不需要真正建立一個二維的並查集。我們可以把這個矩陣進行壓縮,使其成為一個我們喜聞樂見

熟悉的一維並查集。

那麼,對於一個點 \((x, y)\) ,它在並查集陣列中的下標應該是 \(x \times N + y\)

然後,對於每個新新增的節點,我們都會檢測它上、下、左、右四個方位有沒有相同顏色的節點。如果有,那麼繼續判斷該節點是否跟當前節點為同一個祖先。如果不是,那麼我們將它們合併,使其祖先同一。

Code

下面,讓大家來看看這個蒟蒻難看的程式碼啦~

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 510, M = N * N;  // M是最多的矩陣的點數

int n, m, father[M], a[N][N], ans;  // father陣列儲存每個節點祖先,a陣列用於儲存每個節點的顏色,ans變數為當前連通塊的數量
int dx[] = {0, 0 ,1, -1}, dy[] = {1, -1, 0, 0};  // 類似BFS,上下左右四個點的偏移量

int Find(int x) {  // 查詢祖先
    if (father[x] == x) return x;  // 如果祖先是自己,返回
    return father[x] = Find(father[x]);  // 否則遞迴查詢
}

void Union(int u, int v) {  // 合併節點
    int fu = Find(u), fv = Find(v);  // 獲取祖先
    if (fu != fv) father[fu] = fv, ans--;  // 如果祖先不同,那麼合併,並且連通塊數量 - 1
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i < M; i++) father[i] = i;  // 初始化並查集,每個節點的祖先都是自己(即預設有M個連通塊)
    for (int i = 1; i <= m; i++) {
        int c, x, y;
        scanf("%d%d%d", &c, &x, &y);
        a[x][y] = c + 1, ans++;  // c + 1是因為C++預設陣列初始是0,與預設值區分開
        for (int i = 0; i < 4; i++) {  // 迴圈4個方位
            int nx = x + dx[i], ny = y + dy[i];  // 當前新的節點座標
            if (a[nx][ny] != a[x][y]) continue;  // 如果不是同一個顏色那就沒必要合併了
            Union(nx * n + ny, x * n + y);  // 合併節點,判斷祖先的操作在Union函式裡
        }
        printf("%d\n", ans);  // 輸出當前連通塊數量
    }
    return 0;  // 完美結束!
}
```</cstdio></iostream>