# 1092 Algorithm Ex5
###### tags: `courses`, `assignment`
## Algo design
因為懶的自己寫 linked-list 便使用 C++ 的 `vector`
### data structure
使用 adjacent list.
>問題: 在計算 residual graph 時需要同時用到 uv 與 vu
從 uv 得到 vu 需要最多 E 的時間
因此我增加 `invertIndex` 在 edge 裡面,紀錄與自己成對的那個邊的 index
(由於我用 `vector` 不是 linked-list 因此使用 index 而不是 pointer,若使用 pointer 會出問題,因為 `vector` 在 `push_back` 時有機會從新 allocate memory)
有了 `invertIndex` 便可以以 $O(1)$ 的時間查找到與自己向對應的 edge
```graphviz
digraph {
rankdir=LR;
0 -> 1 [dir="none"]
0 -> 2 [dir="none"]
1 -> 3 [dir="none"]
2 -> 3 [dir="none"]
}
```
```graphviz
digraph {
node [shape="record"]
rankdir=LR;
arr [label="<0>\n0\n|<1>\n1\n|<2>\n2\n|<3>\n3\n\n"]
a0 [label="{1|2}"]
a1 [label="{0|3}"]
a2 [label="{0|3}"]
a3 [label="{1|2}"]
arr:0->a0
arr:1->a1
arr:2->a2
arr:3->a3
label="普通的 adjacent list"
}
```
```python=
g[3][0].to == 1 # 得知 edge(3,1) 存在
for i := 0 ~ g[1].size
if g[1][i] == 3
# g[1][i] 為 edge(1,3)
# 結論需要迭代整個 g[1] 陣列去尋找 edge(1,3)
```
```graphviz
digraph {
node [shape="record"]
rankdir=LR;
arr [label="<0>\n0\n|<1>\n1\n|<2>\n2\n|<3>\n3\n\n"]
a0 [label="{1 (0)|2 (0)}"]
a1 [label="{0 (0)|3 (0)}"]
a2 [label="{0 (1)|3 (1)}"]
a3 [label="{1 (1)|2 (1)}"]
arr:0->a0
arr:1->a1
arr:2->a2
arr:3->a3
label="紀錄 invert index"
}
```
```python=
g[3][0].to == 1 # 得知 edge(3,1) 存在
invertIndex = g[3][0].invertIndex
g[1][invertIndex] == 3 # 找到 edge(1,3)
```
### 演算法
#### 找 global min-cut
使用 Ford Fulkerson,用 dfs 尋找 arumenting path。
固定一點 u 對其他所有 v 做一次 FordFulkerson(u, v),找出最小的 min-cut
最小的 min-cut 便是 global min-cut
#### 找 min-cut edge
假設目前的 residual graph (簡稱 $G_r$) 就是 global min-cut 的 $G_r$
那麼用做到最後的 $G_r$ 從 src 開始進行一次 DFS,會得知一些 vertex 為 reachable 一些為 unreachable,而在 reachable vertex 與 unreachable vertex 之間的 edge 及為 min-cut edge
可以理解成從 src 開始往 sink 那半邊,走到走不過去時,那些擋住你的 edge 就是導致塞車的關鍵點。
### pseudo code
```python=
FordFulkerson(rG, src, sink):
mincut := 0
visited := [0, 0, ...]
while true
if dgsArgPath(rG, visited, src, sink) == true:
mincut += 1 # 每次必定只會增加ㄧ
else:
break
return mincut
dfsArgPath(rG, visited, v, sink):
for i := 0 ~ rG[v].size
if rG[v][i].residual > 0:
if rG[v][i].to == sink:
foundPath = true
else if visited[rG[v][i].to] == false
visited[rG[v][i].to] = true
dfsArgPath(rG, visited, rG[v][i].to, sink)
if foundPath:
rG[v][i].residual -= 1
rG[v][i].invertEdge.residual += 1 # 更新 residual graph
return true
findMinCutEdge(G, rG, src, sink):
visited = [0, 0...]
ans = []
ForFulkerson(rG, src, sink) # 做一次 fordfulkerson 以獲得 residual graph
dfs(rG, visited, src)
for i := 0 ~ |V| # for each vertex
if visited[i]
for j := 0 ~ G[i].size # for each edge connected to this vertex
if visited[G[i][j].to] == false
# vectex i is reachable and vertex G[i][j].to is unreachable
ans.add(edge(i, G[i][j].to))
return ans
main(G):
src = 0
rG = transform(G) # 將無向圖 G 換成雙向, capicity 為 1 得 residual graph
for sink := 1 ~ |V|
reset rG
if globalMincut > Fordfulkerson(rG, src, sink):
globalMincut = Fordfulkerson(rG, src, sink)
finalSink = sink
mincutEdge = findMinCutEdge(G, rG, src, sink)
```
## Analyze
一次 argumenting path 最糟就是把每一邊都走過:$O(E)$
一次 argumenting path 可以增加 1 flow. 在無向無權重圖中,flow 最多等於 $E$
因此最多做 $E$ 次 arumenting path
因此 一次 Ford Fulkerson 最糟 $O(E^2)$
為了找 global min-cut 我們做了 $V$ 次 FordFulkerson: $O(VE^2)$
找 min-cut edge 需要一次 Ford Fulkerson 並 dfs 一次圖,再測試所有的邊是否是 min-cut edge
$O(E^2 + E + E) = O(E^2)$
整體:$O(VE^2 + E^2) = O(VE^2)$