<style> .reveal .slides { text-align: left; font-size:30px } </style> # Advanced Graph Algorithm ---- - Biconnected Component - Strongly Connected Components - 2-SAT - Maximum Clique - Minimum Mean Cycle - Dominator Tree ---- ## 講義勘誤 7-1. 支配樹能求解對象為 "有向圖",而非 "DAG"。 --- ## Biconnected Component ### 點雙連通分量 ---- 無向圖上,不會產生割點的連通分量 稱為點雙連通分量 <div style="position: absolute; right: 0%; top:30%;"> ![](https://i.imgur.com/4STUEuW.png =300x) </div> ---- ## 名詞定義 ### Articulation (關節點): 讓圖保持連通不可或缺的點 維持多個不同的連通分量,如果拔掉那個點, 則圖會分成多個不同的連通塊 以右圖為例,拔掉點 2 則圖會不連通 ### Bridge (橋): 讓圖維持連通的邊, 如果拔掉那條邊,則圖會不連通 以右圖為例,拔掉連接 1 5 的邊則圖會不連通 <div style="position: absolute; right: -10%; top:30%;"> ![](https://i.imgur.com/nCme5m0.png =300x) </div> ---- ## Tarjan algorithm <div style="position: absolute; right: 70%; top:80%;"> ![](https://i.imgur.com/nCme5m0.png =300x) </div> <div style="position: absolute; right: 10%; top:80%;"> ![](https://i.imgur.com/yxZDuUP.png =300x) </div> ---- ## 回傳值 二維vector,分別為連通分量跟橋 vector size = 2 的為橋 vector size \> 2 的為點雙連通分量 ``{ {0,2,4,6,7}, {2,3,5}, {1,5} }`` ![](https://i.imgur.com/iKRgDRv.png =300x) ---- ## 求關節點 可以發現如果一個點是關節點,則他會存在聯繫多個連通分量, 因此就直接判斷每個點出現次數,如果出現在 > 1 個連通分量中, 則此點為關節點 ![](https://i.imgur.com/iKRgDRv.png =300x) ---- [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/graph/bcc_vertex.cpp) ```cpp= #define PB push_back #define REP(i, n) for(int i = 0; i < n; i++) ``` - init(n); - 初始化圖 - addEdge(u, v); - 建一條無向邊 (u, v) - solve(); - 跑 BCC - 回傳二維 vector,每個一維 vector 是一個連通分量 ---- ### 例題 --- [Critical Structures (2019 ICPC Taipei Site)](https://codeforces.com/gym/102835) 給一張無向圖,找出 - 關節點的數量 - 橋的數量 - 有幾個點雙連通分量 - 最多邊的連通分量有幾條邊 $1\le n\le 1000$ $1\le m\le 10^6$ ---- ### 關節點的數量 & 橋的數量 這兩個用模板前述作法即可求出 ---- ### 點雙連通數量 即為回傳的 vector.size() 即為點雙連通數量 ---- ### 最多邊的連通分量有幾條邊 遍歷所有連通分量, 看每個連通分量內的邊有幾個 ---- 因此,只需要會套模板即可通過這題 [PI 賽中只有 14/101 隊通過](https://raw.githubusercontent.com/jakao0907/CompetitiveProgrammingCodebook/master/scoreboard/icpc2020.png) --- ## strongly connected components (SCC) ### 強連通分量 在有向圖裡的任兩點 $u、v$, 皆存在至少一條 $u$ 到 $v$ 的路徑以及 $v$ 到 $u$ 的路徑 則為強連通分量 <div style="position: absolute; right: -5%; top:30%;"> ![](https://i.imgur.com/aoq7wDU.png =300x) </div> ---- ## kosaraju's algorithm 把一張有向圖,拆成很多個強連通分量 ![](https://i.imgur.com/Hdxts93.png =300x) $\to$ ![](https://i.imgur.com/co9Tojq.png =300x) ---- ## 縮點 縮點之後原本的環就都會不見,變成有向無環圖(DAG) ![](https://i.imgur.com/co9Tojq.png =300x) $\quad\to\quad$ ![](https://i.imgur.com/4nUD24D.png =300x) ---- [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/graph/kosaraju.cpp) ```cpp= #define PB push_back #define FZ(x) memset(x, 0, sizeof(x)) //fill zero ``` - init(n); - 初始化圖 - addEdge(u, v); - 建一條有向邊 (u, v) - solve(); - 跑 SCC 結果存在 bln 陣列中,代表每個點所屬於哪個強連通分量 nScc 代表有幾個強連通分量 ---- ![](https://hackmd.io/_uploads/BJmZROWjh.png =400x) $\quad\to\quad$ ![](https://hackmd.io/_uploads/SyzibYZin.png =422x) 複雜度 $O(n+m)$ ---- ### [CSES --- Planets and Kingdoms](https://cses.fi/problemset/task/1683) n 個城市,m 條單向道路, 如果兩個城市彼此可走到對方,則屬於同一個王國, 問總共有幾個王國? 並且對於每個王國,輸出任意一個城市。 ---- 王國數量即為 nScc, 對於每一個王國,只需要找每個 bln 裡面的其中一個節點即可 ---- ## SCC + DP 由於 SCC 經過縮點之後會變成一張 DAG 因此很多一般有向圖上的問題,轉成 DAG 後即可在 DAG 上 DP ---- ## [CSES --- Coin Collector](https://cses.fi/problemset/task/1686) 給定一張有向圖,每個節點上有 $k_i$ 個硬幣, 如果選擇一條路徑 (可以經過重複節點) 走,最多可以拿到多少硬幣 ? ---- 會發現這題跟上學期 DP on DAG 的作業&期中考長很像 [Mango Cake](https://vjudge.net/contest/544447#problem/C) 只是一題是在 DAG 上,一題在一般有向圖上 ---- 而我們只需要把有向圖縮點後轉成 DAG, 把同一個環上的點權重總和加起來 ![](https://hackmd.io/_uploads/SyzibYZin.png =400x) $\quad\to\quad$ ![](https://hackmd.io/_uploads/SyLoA0vsn.png =400x) 即為 DP on DAG ---- ### 實作 把同一個強連通分量的點合併成同一個點, 可以直接用 bln 當成新圖的編號建圖 用 bln 建出的新圖跑 DP on DAG ```cpp= // 把原本的點權合併到 bln 的點上 for(int i = 0; i < n; i++) sum[scc.bln[i]] += w[i]; // 用 bln 建新圖 for(int i = 0; i < m; i++){ auto [u, v] = edge[i]; if(scc.bln[u] != scc.bln[v]) new_edge[scc.bln[u]].push_back(scc.bln[v]); } ``` --- ## 2-SAT ### 2-satisfiability problem 有 $N$ 個 boolean 變數 $a_1 \sim a_N$ 接著有 $M$ 條需要滿足的式子 ($a_i$ is `true/false` or $a_j$ is `ture/false`) 問有沒有可能決定 $N$ 個變數分別為 `true/false` 的情況下, 滿足 $M$ 條式子 $(\neg a_1\ or\ a_2)\ and\ (a_2\ or\ a_3)\ and\ (\neg a_3\ or\ \neg a_4)$ ---- $(\neg a_1\ or\ a_2)\ and\ (a_2\ or\ a_3)\ and\ (\neg a_3\ or\ \neg a_1)$ - $\neg a_1\ or\ a_2$ (滿足 $a_1$為false 或者 $a_2$為true) - $a_2\ or\ a_3$ (滿足 $a_2$為true 或者 $a_3$為true) - $\neg a_3\ or\ \neg a_1$ (滿足 $a_3$為false 或者 $a_1$為false) 一個合法解為 $a_1 = true, a_2 = true, a_3 = false$ ---- ## 舉個例子 [Giant Pizza](https://cses.fi/problemset/task/1684) 有 $N$ 個要訂披薩,披薩有 $M$ 種配料 每個人有 2 個願望,願望可能為 (希望/不希望披薩出現配料 $a_i$ ) 問每個配料加或不加,是否能滿足每個人至少一種願望 #### sample input ```txt 3 5 + 1 + 2 - 1 + 3 + 4 - 2 ``` #### sample output ```txt - + + + - ``` (配料1要加 or 配料2要加) and (配料1不要加 or 配料3要加) and (配料4要加 or 配料2不要加) ---- 會發現如果要暴力找到一組合法解最差情況要花 O($2^N\cdot M$) $N$個變數窮舉 true/false, 再去檢查是否滿足 $M$ 個式子 但題目的 $N, M$ 為 $10^5$ $\to$ TLE ---- ## 轉成圖論 scc 作法 2-SAT 問題可以轉換成 SCC 去求解 只需把每個狀態轉成節點/邊即可 ---- 每個變數有 True, False 兩種可能的狀態 也就是 $a_i$ ($i$ = True) 或者 $\neg a_i$ ($i$ = False) 以剛剛那題就是要加第 $i$ 種配料或者不加第 $i$ 種配料 ---- 把每個變數的兩種狀態 True/False 轉成圖論的節點編號 | 狀態\變數 | 1 | 2 | 3 | | -- | --- | --- | --- | | True | 1 | 2 | 3 | | False | 4 | 5 | 6 | 節點 1 為變數 1 的 True 狀態 節點 2 為變數 2 的 True 狀態 節點 3 為變數 3 的 True 狀態 節點 4 為變數 1 的 False 狀態 節點 5 為變數 2 的 False 狀態 節點 6 為變數 3 的 False 狀態 ---- 對於一個限制 (x or y) 則加兩條邊 - x $\to$ $\neg$y - y $\to$ $\neg$x ---- ## 建邊 ```txt 3 5 + 1 + 2 - 1 + 3 + 4 - 2 ``` | 狀態\變數 | 1 | 2 | 3 | 4 | 5 | | -- | --- | --- | --- | --- | --- | | True | 1 | 2 | 3 | 4 | 5 | | False | 6 | 7 | 8 | 9 | 10 | <div style="position: absolute; right: 70%; top:120%;"> - `+1(1) +2(2)` add_edge(1, 7) add_edge(2, 6) </div> <div style="position: absolute; right: 40%; top:120%;"> - `-1(6) +3(3)` add_edge(6, 8) add_edge(3, 1) </div> <div style="position: absolute; right: 10%; top:120%;"> - `+4(4) -2(7)` add_edge(4, 2) add_edge(7, 9) </div> ---- ## 求解 建完圖之後跑 scc,對於每個變數 判斷每個變數兩種狀態(true/false) 的 bln 如果 true 狀態的 bln 比較小則為 true - scc.bln[i] < scc.bln[not i] 否則 false 狀態的 bln 比較小則為 false - scc.bln[not i] < scc.bln[i] 如果 true 狀態的 bln 和 false 狀態的 bln 相等,則無法滿足所有條件 - scc.bln[i] == scc.bln[not i] 都符合則找到一組合法解 ---- ## 複雜度 N 個變數分別兩種狀態 2N 建 2M 條邊 再跑SCC 複雜度 $O(N+M$) 通常題目如果只有兩種狀態,要或不要,塗黑色或白色...等 並且求一組合法解就有可能是 2-SAT 想想看能不能把條件轉成 $(\neg a_1\ or\ a_2)\ and\ (a_2\ or\ a_3)\ and\ (\neg a_3\ or\ \neg a_1)$ 即可做 ---- ### 另一個例題 ### [2022 NCPC Final --- G. Rounding](https://e-tutor.itsa.org.tw/e-Tutor/mod/programming/view.php?id=65416) 給一個 $N\times N$ 的數字矩陣 $a$,每個 row/column 各自**最多**兩個浮點數,其他都為整數 $\left( \begin{array}{ccc} 6.3 & 7 & 4.6\\ 4.7 & 2 & 2 \\ 5 & 4.5 & 3.4 \\ \end{array} \right)$ 要把每個小數都選擇進位或捨去,變成矩陣 $b$ ---- 使得最後滿足 $\forall i =\{1,2,3,...n\}, \sum\limits_{j=1}^{n} b_{ij} = \lfloor \sum\limits_{j=1}^{n} a_{ij}\rfloor$ or $\sum\limits_{j=1}^{n} b_{ij} = \lceil \sum\limits_{j=1}^{n} a_{ij}\rceil$ $\forall j =\{1,2,3,...n\}, \sum\limits_{i=1}^{n} b_{ij} = \lfloor \sum\limits_{i=1}^{n} a_{ij}\rfloor$ or $\sum\limits_{i=1}^{n} b_{ij} = \lceil \sum\limits_{i=1}^{n} a_{ij}\rceil$ 每個 row/column 的總和等於原始數值總和的上或下取整 ---- $\left( \begin{array}{ccc} 6.3 & 7 & 4.6\\ 4.7 & 2 & 2 \\ 5 & 4.5 & 3.4 \\ \end{array} \right)$ $\to$ $\left( \begin{array}{ccc} 7 & 7 & 4\\ 4 & 2 & 2 \\ 5 & 4 & 4 \\ \end{array} \right)$ 每個 row/column 的總和等於原始數值總和的上取整或下取整 ---- ## 轉換問題 變成原始矩陣的上取整或下取整,可以分成以下幾種 case: - 兩個小點數加起來 $> 1$,則兩個浮點數轉換至少一個進位 - 兩個小點數加起來 $= 1$,則兩個浮點數轉換恰好一個進位 - 兩個小點數加起來 $< 1$,則兩個浮點數轉換至多一個進位 $\left( \begin{array}{ccc} 4.2 & 5.6 \\ 1.1 & 3.9 \end{array} \right)$ 4.2 + 5.6 = 9.8 $\to$ 9 or 10 1.1 + 3.9 - 5 $\to 5$ 5.6 + 3.9 = 9.5 $\to$ 9 or 10 ---- 每個小數是否進位轉換為狀態 $a_i$ = True 代表進位 $a_i$ = False 代表不進位 ---- 兩個浮點數 $i, j$ 轉換至少一個進位 - $a_i =$ True $\vee$ $a_j =$ True ---- 兩個浮點數 $i, j$ 轉換恰好一個進位 - (a_i =$ True $\vee$ $a_j =$ True) $\land\ (a_i =$ False $\vee$ $a_j =$ False) ---- 兩個浮點數 $i, j$ 轉換至少一個不要進位 - $a_i =$ False $\vee$ $a_j =$ False ---- 根據每個 row/column 的情況轉換 - 兩個小點數加起來 $> 1$,則兩個浮點數轉換至少一個進位 - 兩個小點數加起來 $= 1$,則兩個浮點數轉換恰好一個進位 - 兩個小點數加起來 $< 1$,則兩個浮點數轉換至多一個進位 建圖跑 SCC 即可求解 ---- 對於條件 (X or Y) 如果覺得建邊 ($X\to \neg Y$) ($Y\to \neg X$) 很難記的話,可以參考這個記法: 不選 X,就要選Y 不選 Y,就要選X 或許會比較好記一點 不過還是建議把建邊方法放模板以防太久沒用忘記 --- ## 最大獨立集 一張圖上,最多能選幾個點,使得選的點彼此都不沒有連邊 ![](https://i.imgur.com/7sOfUyJ.png) 以上圖為例,選 3、5、7、8 四個點為一組解 ---- ## 最大團 一張圖上,最多可以選幾個點,使選的彼此之間都有連邊 ![](https://i.imgur.com/sb5gVYW.png) 以上圖為例,選 1、2、5、6 四個點為一組解 ---- 最大獨立集本身沒有好的做法,只能窮舉 subset 複雜度 $O(2^{N}M)$ 而最大團目前最佳複雜度為 $O(1.1888^n)$ 因此最大獨立集通常會轉換為用補圖做最大團 ---- ## 作法 獨立集 與 團 的概念完全相反 最大獨立集去建反圖之後,求最大團即可得到答案 <div style="position: absolute; right: 70vh; top: 38vh"> $\to$ </div> <div style="position: absolute; right: 70%;"> ![](https://i.imgur.com/9qqkv4L.png) </div> <div style="position: absolute; right: 25%;"> ![](https://i.imgur.com/lGn2mzD.png) </div> ---- [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/graph/MaximumClique.cpp) - init(n); - 初始化圖 - addEdge(u, v); - 建一條雙向邊 (u, v) - solve(); - 回傳值為最大團的點數量 $O(1.1888^n)$,約可以做到 n 為 100 多 通常題目最大會出到 80-100 左右 ---- ### [2013 ICPC Chia-Yi --- A Generalized N-Queens Problem](https://vjudge.net/contest/633971#problem/J) 給一個 $N\times N$ $(N\le 10)$ 的網格圖,有些格子已經放障礙物了。 圖上最多可以放幾個皇后,才能不會攻擊到對方。 ![](https://hackmd.io/_uploads/H1vUHSroh.png) 上圖為例可以放三個 ---- 會發現 N 特別小,整個圖的大小只有 100 最大獨立集? ---- 如果兩個皇后位於同一 row/column/diagonal , 並且中間沒有障礙物的話不能同時存在, 我們可以把每個非障礙物的位置當成一個節點,互相可以攻擊到對方的位置連邊跑最大獨立集,即為答案 ---- ## 極大團 對於一張圖,選任意的點子集,如果不能在多選一個點使得選的點子集為更大的團 ![](https://i.imgur.com/oqMC6X8.png =300x) 以上圖為例 {1, 4} 為一個極大團 {2, 3} 並非極大團,因為可以再多加點 6 變成更大的團 {1, 2, 3}, {2, 3, 6} {3, 5, 6} 也為極大團 ---- 程式碼中會不斷窮舉極大團 每次跑到第 20 行時,cans(bitset) 中值為 1 的節點代表在當前窮舉的極大團裡 ```cpp= [|6-10|11-12|32-44|20|] #define N 80 struct MaxClique{ // 0-base typedef bitset<N> Int; Int lnk[N] , v[N]; int n; void init(int _n){ n = _n; for(int i = 0 ; i < n ; i ++){ lnk[i].reset(); v[i].reset(); } } void addEdge(int a , int b) { v[a][b] = v[b][a] = 1; } int ans , stk[N], id[N] , di[N] , deg[N]; Int cans; void dfs(int elem_num, Int candi, Int ex){ if(candi.none()&&ex.none()){ cans.reset(); for(int i = 0 ; i < elem_num ; i ++) cans[id[stk[i]]] = 1; ans = elem_num; // cans is a maximal clique return; } int pivot = (candi|ex)._Find_first(); Int smaller_candi = candi & (~lnk[pivot]); while(smaller_candi.count()){ int nxt = smaller_candi._Find_first(); candi[nxt] = smaller_candi[nxt] = 0; ex[nxt] = 1; stk[elem_num] = nxt; dfs(elem_num+1,candi&lnk[nxt],ex&lnk[nxt]); } } int solve(){ for(int i = 0 ; i < n ; i ++){ id[i] = i; deg[i] = v[i].count(); } sort(id , id + n , [&](int id1, int id2){ return deg[id1] > deg[id2]; }); for(int i = 0 ; i < n ; i ++) di[id[i]] = i; for(int i = 0 ; i < n ; i ++) for(int j = 0 ; j < n ; j ++) if(v[i][j]) lnk[di[i]][di[j]] = 1; ans = 1; cans.reset(); cans[0] = 1; dfs(0, Int(string(n,'1')), 0); return ans; } }solver; ``` --- ## Minimum Mean Cycle 給定一張有向圖,邊上有權重,要找到一個環其平均權重最小。 ![](https://hackmd.io/_uploads/BJKvHWrj2.png =400x) 例圖中最小平均環為 2->3->4->2 平均為 $\frac{5}{3}$ ---- ### 2018 ICPC Taipei --- J. Mean Weight of a Critical Cycle 裸題, 給定一張有向圖,求出最小平均環,用最簡分數表示。 ---- [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/graph/MinMeanCycle.cpp) - init(n); - 初始化圖 - addEdge(u, v, w); - 建一條單向邊 (u, v) 權重為 w - solve(); - 回傳值為最小平均權重 (小數) 複雜度 $O(VE)$ ---- [賽中記分板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/scoreboard/icpc2018.jpg) 18/116 通過 --- ## Dominator Tree 基本定義: 給一張有向圖,圖上有一個起點 $S$ 可以走到所有點。 定義 "支配" 為從起點 $S$ 出發,所有能走到節點 $x$ 的路徑的最後一個必經點。 ![image](https://hackmd.io/_uploads/rJKFsSXv0.png =500x) 以節點 6 來說,最後一個必經點為 5 以節點 4 來說,最後一個必經點為 0 以節點 3 來說,最後一個必經點為 2 ---- ![image](https://hackmd.io/_uploads/rJKFsSXv0.png =500x) 以節點 6 來說,支配點為 5 以節點 4 來說,支配點為 0 以節點 3 來說,支配點為 2 除了起點 $S$ 以外,其他點都有支配點。 ---- [模板](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/graph/DominatorTree.cpp) 用法 1. init(n, s); // 節點數量,起點編號 1-base 2. addEdge(u, v); // 建邊 3. build(); // 建立支配樹 最後 $idom[i]$ 為點 $i$ 的支配點 複雜度 $O(n+m)$ ---- ## 經典題 給一張 DAG,求從 1 號節點出發,每個節點能支配的點的個數 ![image](https://hackmd.io/_uploads/BkpKCSmwR.png) 2 號節點能支配的點為 1 4 號節點能支配的點為 1, 2, 3 8 號節點能支配的點為 1, 2, 3 9 號節點能支配的點為 1, 2, 3, 4, 7 ---- 把每個點的支配點當成父節點,形成的圖為一棵樹。 所有要求的問題即可轉為樹上問題。 ![image](https://hackmd.io/_uploads/rkTevlsvR.png) ---- 每個節點能支配的點的個數轉成樹上問題, 即為從當前點到根節點的距離總和。 ---- 題單: https://vjudge.net/contest/633971 好用小工具: https://csacademy.com/app/graph_editor/
{"title":"Advanced Graph Algorithm","contributors":"[{\"id\":\"19f09ccf-6b99-452f-971f-955cfc1657f3\",\"add\":13404,\"del\":472}]","description":"Biconnected Component"}
    1055 views
   Owned this note