# 連通塊 Connected Component
此章節接續 [DFS 與 BFS](https://hackmd.io/@ShanC/bfs_dfs)、[併查集](https://hackmd.io/@ShanC/dsu),要來聊聊該如何使用先前的內容來找連通塊
**本篇的圖皆為 undirected simple graph**
## 連通性 Connectivity
關於一張圖的連通性,有以下定義:
設有一張圖 $G$
- 假如 $G$ 是「連通的」,那 $G$ 就必存在一條 $(u, v)$ 路徑,否則就是「不連通的」
- 假如 $G$ 有一條 $(u, v)$ 路徑,則 $u$ 與 $v$ 「連通」
- 假如有一條 $(u,v)$ 邊,則 $u$ 與 $v$ 「相鄰」
```graphviz
graph hierarchy {
node [shape="circle"]
layout=fdp
size = "4,3"
a -- b
a -- c
c -- b
d -- e
}
```
以上圖為例,$a$ 與 $c$ 連通,但 $b$ 與 $e$ 不連通;$a$ 與 $c$ 連通,因此存在至少一條路徑
## 連通塊 Connected Component
### 連通塊定義 The Definition of Connected Component
連通塊又稱連通分量、連通單元、連通成分,定義如下:
- 設一張圖 $G$,其連通塊為 $G$ 的極大 (maximal) 連通子圖 (subgraph)
- 若有一連通塊無邊 (即只有 $1$ 節點),則稱為顯然的 (trivial) 連通塊
- 若有一連通塊有邊,則稱為非顯然的 (nontrivial) 連通塊
- 若 $G$ 只有一個連通塊,則稱 $G$ 為連通圖
以下圖來說,有兩個連通塊 $\{a,b,c,d,e\}$ 與 $\{f\}$,其中 $\{f\}$ 為顯然連通塊
<center>
<img src="https://hackmd.io/_uploads/r1MdoWiQkg.png" style="margin: 0 auto; display: block; width: 300px">
</center>
### 性質: 連通塊的數量關係
#### 引理
假設 $G$ 有多個連通塊,每增加一條邊,則會減少 $0$ 或 $1$ 個連通塊;每減少一條邊則有可能增加 $0$ 或 $1$ 個連通塊
#### 定理
設一張圖 $G$ 有 $n$ 個節點與 $k$ 條邊,則 $G$ 最少有 $n - k$ 個連通塊
#### 證明
令 $G$ 有 $n$ 個節點與 $0$ 條邊,則 $G$ 有 $n$ 個連通塊。根據引理,每次加一條邊,最多可能減少 $1$ 個連通塊。因此設有 $k$ 條邊,則最少會有 $n - k$ 個連通塊
### 備註
當 $k=n-1$ 時,$G$ 最少會有 $1$ 個連通塊。又根據連通性,$G$ 只有 $1$ 連通塊時,$G$ 為一張連通圖。因此如果 $G$ 是一張連通圖,則至少有 $n-1$ 條邊
## 程式實作
### DFS 版本
#### 性質
1. 設 $C$ 與 $C'$ 為 $G$ 裡的連通塊,其中兩節點 $u \in V(C)$、$v \in V(C')$。如果存在 $(u, v)$ 路徑,則 $C$ 與 $C'$ 為相同的連通塊
2. 假設 $(u,v) \in E(G)$,使用 DFS 走訪 $G$ 時,如果能走訪到 $u$, 就必走訪到 $v$
BFS 也符合性質 2. ,所以也可以用來尋找連通塊,~~但是作者懶得寫~~
#### 程式碼
為了區分連通塊,我們可以將其編號,首先找到的為 $1$ 號,以此類推
- $id[i]$ : 第 $i$ 個節點所在的連通塊編號
- $sz[i]$ : 第 $i$ 個節點所在的連通塊大小
- $g[i]$ : 鄰接陣列,第 $i$ 個節點的鄰居
- $n$ : 節點數量
```cpp
vector<int> g[MXN];
int id[MXN], sz[MXN], n;
void dfs(int u, int k) {
sz[k]++, id[u] = k;
for (int &v : g[u]) {
if (id[v] == 0)
dfs(v, k);
}
}
int find_cc() {
int cnt_cc = 0; // 紀錄連通塊數量
for (int i = 1; i <= n; i++) { // 1-based
if (id[i] == 0) // id[i] == 0 代表尚未走訪
dfs(i, ++cnt_cc);
}
return cnt_cc;
}
```
#### 時間複雜度
$O(m + n)$, 與 DFS 相同
### 併查集實作
#### 性質
併查集的原理與連通性有相似之處,因此可以併查集維護節點的集合 $V$
關於併查集的原理可以看看[這篇](https://hackmd.io/@ShanC/dsu)
#### 程式碼
執行步驟如下:
1. 初始化併查集,將每個節點視為獨立的集合 (因為不知道是否連邊)
2. 對於所有邊 $(u,v)$,將每條邊的終點 $u$ 與 $v$ 合併為同一集合
3. 利用 $find()$ 函數尋找有幾個不同的根 (即集合代表)
```cpp
int f[MAXN], sz[MAXN];
void init(int n) { // 初始化
for (int i = 1; i <= n; i++) { // 1-based
f[i] = i;
sz[i] = 1;
}
}
int find(int x) {
if (f[x] == x) // 如果當前節點為 f[x] == x
return x; // 則為根節點
f[x] = find(f[x]);
return f[x];
}
void merge(int x, int y) { // 將 x 和 y 合併
x = find(x), y = find(y);
if (x == y)
return;
if (sz[x] < sz[y]) // 將小的併入大的
swap(x, y);
sz[x] += sz[y];
f[y] = x;
}
int n; // 節點數
vector<int> g[MAXN]; // 鄰接陣列
void find_cc() {
vector<bool> vis(n + 5, false);
init(n);
int ret = 0;
for (int i = 1; i <= n; i++) { // 尋找所有連邊關係
for (int &j : g[i])
merge(i, j);
}
for (int i = 1; i <= n; i++) // 1-based
vis[find(i)] = true;
for (int i = 1; i <= n; i++) // 遍歷所有集合代表
ret += vis[i];
return ret;
}
```
#### 時間複雜度
- 初始化併查集: $O(n)$
- 尋找所有邊 + 合併: $O(m) \times O(\alpha(n))\approx O(m)$
- 遍歷節點找根: $O(\alpha(n))\times O(n) + O(n)\approx O(n)$
總時間複雜度: $O(m+n)$
## 題目練習
[Zerojudge f670. FJCU_109_Winter_Day1_Lab5 連通塊數量](https://zerojudge.tw/ShowProblem?problemid=f670)
[Zerojudge f260. 愛八卦的同學](https://zerojudge.tw/ShowProblem?problemid=f260)
[Zerojudge f680. 色塊數量](https://zerojudge.tw/ShowProblem?problemid=f680)
[Zerojudge a445. 新手訓練系列- 我的朋友很少](https://zerojudge.tw/ShowProblem?problemid=a445)
[OnlineJudge-459 Graph Connectivity](https://vjudge.net/problem/UVA-459)
[CF contest-2034 C. Trapped in the Witch's Labyrinth](https://codeforces.com/contest/2034/problem/C)
[AtCoder abl_c Connect Cities](https://atcoder.jp/contests/abl/tasks/abl_c?lang=en)
[CSES Counting Rooms](https://cses.fi/problemset/task/1192)
[Zerojudge m372. APCS 3. 搬家](https://zerojudge.tw/ShowProblem?problemid=m372)
[Zerojudge o927. 鐵路 (Rail)](https://zerojudge.tw/ShowProblem?problemid=o927)
[Zerojudge g598. APCS 4. 真假子圖](https://zerojudge.tw/ShowProblem?problemid=g598)
----
## 參考資料
- Introduction To Graph Theory - D. B. West
- Introduction to Algorithms, Fourth Edition
- [SA 流 C++ 競程修練心法](https://hackmd.io/@sa072686/cp/%2F%40sa072686%2FSyceyXTDE)
- [海大競程 併查集](https://hackmd.io/@LeeShoWhaodian/dsu#/2/33)
----
> [ShanC 程式競賽筆記](https://hackmd.io/@ShanC/B1ouGxqcC)
> 作者: ShanC
> 更新: 2024/12/2