<style> .reveal .slides { text-align: left; font-size:30px; } </style> # graph (advanced) ---- - System of Difference Constraints - SCC/BCC - 2-SAT --- ## System of Difference Constraints 差分約束系統 ---- 是求解關於一組不等式之方法。 $n$ 個變數和 $m$ 個約束條件組成 其中每個約束條件形如 ${ x_{j}-x_{i}\leq b_{k}(i,j\in [1,n],k\in [1,m])}$ 則稱其為差分約束系統。 ---- ## 例子 ${x_{1}-x_{2}\leq 5}$ ${x_{1}-x_{3}\leq 1}$ $\to x_{1}=0, x_{2}=-5, x_{3}=-1$ ${x_{1}-x_{2}\leq 1}$ ${x_{1}-x_{3}\leq -1}$ ${x_{3}-x_{2}\leq 0}$ $\to$ 無解 <div style="position: absolute; right: 10%; top:0%;"> ![](https://i.imgur.com/uA2FxQR.png =250x) </div> <div style="position: absolute; right: 10%; top:80%;"> ![](https://i.imgur.com/EaMjJwm.png =250x) </div> ---- ## 作法 轉成圖論問題 對於所有 $x_{j}-x_{i}\leq k$ 連接一條邊 $(i, j)$,權重為 $k$ 最後再設置一個起點 $s$,連向所有邊邊權為 $0$ 從起點 $s$,跑 SPFA ,若出現負環則代表這組不等式無解 ---- ## 建圖 ${x_{1}-x_{2}\leq 1}$ ${x_{1}-x_{3}\leq -1}$ ${x_{3}-x_{2}\leq 0}$ ![](https://i.imgur.com/LySYtE5.png) ---- ## 結果 跑完 SPFA 如果發現有負環,則此組不等式無解 否則所有點到起點的距離為其中一組解 --- ## strongly connected components (SCC) ### 強連通分量 在有向圖裡的任兩點 $u、v$, 皆存在至少一條 $u$ 到 $v$ 的路徑以及 $v$ 到 $u$ 的路徑 則為強連通分量 <div style="position: absolute; right: -10%; top:30%;"> ![](https://i.imgur.com/aoq7wDU.png =300x) </div> ---- ## kosaraju's algorithm 把一張有向圖,拆成很多個強連通分量 <div style="position: absolute; right: 70%; top:80%;"> ![](https://i.imgur.com/Hdxts93.png =300x) </div> <div style="position: absolute; right: 10%; top:80%;"> ![](https://i.imgur.com/co9Tojq.png =300x) </div> ---- ## 縮點 縮點之後原本的環就都會不見,變成有向無環圖(DAG) <div style="position: absolute; right: 70%; top:80%;"> ![](https://i.imgur.com/co9Tojq.png =300x) </div> <div style="position: absolute; right: 10%; top:80%;"> ![](https://i.imgur.com/4nUD24D.png =300x) </div> ---- ## 例題 [Planets and Kingdoms](https://cses.fi/problemset/task/1683/) ### 題意 有 $N$ 個星球,如果兩個星球 $i$ 和 $j$ 彼此都有一條路到達彼此,則他們屬於同一個王國。 幫每個國家都標記一個編號,如果屬於同一個王國則編上同一個編號 ### 作法 對於兩個星期 $i,\ j$ 如果有路可以到彼此則為強連通分量 首先先把圖分成很多個強連通分量,接者看每個星球所屬的 bln 為多少 ---- ```cpp [|6-10|11-13|23-35|4|] #define FZ(x) memset(x, 0, sizeof(x)) #define PB push_back struct Scc{ int n, nScc, vst[MXN], bln[MXN]; // 最後每個點所屬的連通分量存在bln陣列 vector<int> E[MXN], rE[MXN], vec; void init(int _n){ //先初始化點的數量 n = _n; for (int i=0; i<MXN; i++) E[i].clear(), rE[i].clear(); } void addEdge(int u, int v){ // 加有向邊 E[u].PB(v); rE[v].PB(u); } void DFS(int u){ vst[u]=1; for (auto v : E[u]) if (!vst[v]) DFS(v); vec.PB(u); } void rDFS(int u){ vst[u] = 1; bln[u] = nScc; for (auto v : rE[u]) if (!vst[v]) rDFS(v); } void solve(){ // 跑 kosaraju nScc = 0; vec.clear(); FZ(vst); for (int i=0; i<n; i++) if (!vst[i]) DFS(i); reverse(vec.begin(),vec.end()); FZ(vst); for (auto v : vec) if (!vst[v]){ rDFS(v); nScc++; } } }; ``` ---- ## Biconnected Component (BCC) ### 點雙連通分量 無向圖上,不會產生割點的連通分量 稱為點雙連通分量 <div style="position: absolute; right: -10%; 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,分別為連通分量跟橋 ``{ {0,2,4,6,7}, {2,3,5}, {1,5} }`` ![](https://i.imgur.com/iKRgDRv.png =300x) ---- ## 求關節點 可以發現如果一個點是關節點,則他會存在聯繫多個連通分量, 因此就直接判斷每個點出現次數,如果出現在多於一個連通分量, 連接著多個連通分量,則此點為關節點 ![](https://i.imgur.com/iKRgDRv.png =300x) ---- ## 例題 https://zerojudge.tw/ShowProblem?problemid=c111 找幾個關節點 ---- ```cpp [|5-8|9-10|31-42|] struct BccVertex { int n,nScc,step,dfn[MXN],low[MXN]; vector<int> E[MXN],sccv[MXN]; int top,stk[MXN]; void init(int _n) { //初始化點的數量 n = _n; nScc = step = 0; for (int i=0; i<n; i++) E[i].clear(); } void addEdge(int u, int v) // 加無向邊 { E[u].PB(v); E[v].PB(u); } void DFS(int u, int f) { dfn[u] = low[u] = step++; stk[top++] = u; for (auto v:E[u]) { if (v == f) continue; if (dfn[v] == -1) { DFS(v,u); low[u] = min(low[u], low[v]); if (low[v] >= dfn[u]) { int z; sccv[nScc].clear(); do { z = stk[--top]; sccv[nScc].PB(z); } while (z != v); sccv[nScc++].PB(u); } }else low[u] = min(low[u],dfn[v]); } } vector<vector<int>> solve() { // 跑 Tarjan vector<vector<int>> res; for (int i=0; i<n; i++) dfn[i] = low[i] = -1; for (int i=0; i<n; i++) if (dfn[i] == -1) { top = 0; DFS(i,i); } REP(i,nScc) res.PB(sccv[i]); return res; } }graph; ``` ---- SCC、BCC 非常重要 台灣站很常出 一個圖論題目加難的方法就是把題目變成需要用到這兩個 遇到有環可能會很難的題目,記得想想看能不能用SCC/BCC簡化他 --- ## 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 個願望,願望可能為 (希望/不希望披薩出現配料) 問每個配料加或不加,是否能滿足每個人至少一種願望 #### 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 作法 把每個變數分別轉成兩種狀態 true/false 並且把這兩種狀態編號起來 | 兩種狀態\N個變數 | 1 | 2 | 3 | | -- | --- | --- | --- | | True | 1 | 2 | 3 | | False | 4 | 5 | 6 | 把每種狀態分別當成圖論上的節點 對於一個限制 (x or y) 則加兩條邊 - x $\to$ $\neg$y - y $\to$ $\neg$x ---- ## 建邊 ```txt 3 5 + 1 + 2 - 1 + 3 + 4 - 2 ``` | 兩種狀態\N個變數 | 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 比較小則為 ture 否則false狀態的 bln 比較小則為 false 每個變數得到狀態後去驗證 $M$ 個條件, 如果其中一個條件不符合則代表無法滿足所有條件 都符合則找到一組合法解 ---- ## 複雜度 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)$ 即可做 --- ### Question Time and Practice [Homework Link](https://vjudge.net/contest/498988) 提交期限到下星期三 6/15 23:59 截止
{"metaMigratedAt":"2023-06-17T02:23:38.492Z","metaMigratedFrom":"YAML","title":"graph (advanced)","breaks":true,"slideOptions":"{\"transition\":\"fade;\"}","contributors":"[{\"id\":\"19f09ccf-6b99-452f-971f-955cfc1657f3\",\"add\":7901,\"del\":464}]"}
    508 views