洛谷P5877 棋盤遊戲
阿新 • • 發佈:2021-08-22
洛谷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>