On Github bobogei81123 / ioi-camp-2016-flow-slides
想像一下…
這些東西都有這些特性
而我們定義這個 s-t 流的流量為 \( \abs{f} = \sum_{v \in V} f(s, v) \)
這些是上限 , 想像成前面提到的 網路等等。
算一下
流量最大的一個流,也就是最大流!
一點一點慢慢增加流量
增加容量,找「有空的水管」,有空就是還有多的容量找到後應該可以增加一點
這是一開始的狀況,把所有可以流的抓出來
問:可以增加多少流量?
瓶頸會是最小的一條!
圖上那條不見了.
直到有一天流不了。
s 走不到 t
Question: 這樣是否就是一個最大流?
Answer: No!
很簡單的反例, 最大流 2 綠色是流的邊,這些都不能再流了。
還記得流量對稱 : \( f(u, v) = -f(v, u), \; \forall (u, v)\)
其實快對了,一直沒有用到這個條件。
\( -3 < 0 \) ,可以流過去!
多了中間的邊
Question: 考慮負向邊之後,是否就是一個最大流?
Answer: Yes!
沒騙你!
從剛剛我們知道,有剩的邊是關鍵!
在剩餘網路上不斷找路增加流量!
假設找到了一條路徑 \( P = s \to v_1 \to v_2 \to \cdots \to v_n \to t \)。
有一個廢到有剩的邊。 你的流量增加,反向減少。
\( f, c, r \) 知其二則剩下一個也可以推出。
其實可能只要記 \( r \) 就好。
看上面發現可不可以流只看 r 即可。 min r(e) 也只跟 r 有關。 如果不用輸出所有邊的流,記 r 即可不斷的找路徑擴充流量,直到剩餘網路沒有\( s \leadsto t \) 的路徑,就是最大流!
我們先進行一段 C++ Coding 宣導。
分享一些 coding 小心得。(其實我 coding 能力低落)寫個 struct
struct Edge { int u, v, f, c; }; Edge e = {1, 2, 3, 4};
typedef pair<int, int> Edge; Edge e = {1, 2};
寫鍊表
或是用 vector
vector<Edge> E[VMAX]; // u -> v E[u].PB({v, 1, 2}); // u <-> v E[u].PB({v, 1, 2}); E[v].PB({u, 1, 2});
Flow 我們會需要查反邊
vector<Edge> E[VMAX]; #define SZ(a) ((int)(a).size()) E[u].PB({v, 1, 2, SZ(E[v]) }); E[v].PB({u, 1, 2, SZ(E[u])-1});
C++11 以上
vector<Edge>::iterator it = E[v].begin(); auto it = E[v].begin();
C++11 以上
for (auto it=E[v].begin(); it!=E[v].end(); it++) { int u = *it.u; } for (auto u: E[v]) { }
G++ only
#include <bits/stdc++.h>
int a[5] = {0}; int a[5] = {}; int a[5] = {1, 2, 3}; // 1, 2, 3, 0, 0
bool cmp(int i, int j) { return i > j; } sort(vector.begin(), vector.end(), cmp); sort(vector.begin(), vector.end(), [](int i, int j) -> bool { return i > j; });
bool cmp(Edge e1, Edge e2) { if (e1.u != e2.u) { return e1.u < e2.u; } return e1.v < e2.v; } bool cmp(Edge e1, Edge e2) { return tie(e1.u, e1.v) < tie(e2.u, e2.v); // Or return make_tuple(e1.u, e1.v) < make_tuple(e2.u, e2.v); }
tuple<int, int, int> t = {1, 2, 3}; int a, b, c; tie(a, b, c) = t;
int f = 0 while (tf = find_path()) { f += tf; } return f;
看起來很簡單重點是如何 find_path
int find_path(int u = S, int curf = INF) { if (u == T) return curf; // Done! for (auto &e: E[u]) { int v = e.dest, f = e.f, tf; if (tf = find_path(v, min(curf, f))) { auto rev = e.rev; e.f -= tf; ref.f += tf; return tf; } } return 0; }
要找反邊一加一減接下來的演算法都類似
沒有保證!
在流量是整數的時候,每次流量至少增加 \( 1 \), 複雜度 \( \ord{E \abs{f}} \)。
剛剛那個反例就是最糟的情況
int f = 0 while (tf = find_shortest_path()) { f += tf; } return f;
有差嗎?
只差在 Edmonds-Karp 每次規定找最短的一條擴充路徑。
差多少呢?
差非常多! 複雜度變成 \( \ord{V E^2} \)不但跟流量大小無關,無理數也會 work !
本來 s -> u 的邊慢慢不見變 u -> s,合理
Edmonds-Karp 的一個小優化。
每次擴充後其實只有一些點的 \( d(v) \) 會變。
一次把所有長度是 \( k \) 的擴充路徑都擴充完。
畫分層圖
每次 \( \ord{k E} \),總共 \( \ord{V^2 E} \)。
一次 sum deg()
裸的最大流,BJ4
把一條路徑想成是一條流
我們先說一下定義
二分圖匹配有不少應用
觀察一個路徑 \( v_1 \to v_2 \to \cdots \to v_n \)
每一個中間走到的點 \( +2 \) ,兩端 \( +1 \)。
度數奇偶是關鍵一個有向圖有歐拉迴路的條件是所有點都滿足 \( \deg^+(v) = \deg^-(v) \),
混合圖呢?
進來 = 出去,合理。
其實就是要把每一個無向邊都定向成有向圖
最後要求每一個點的 \( \deg^+(v) = \deg^-(v) \)。
令還沒有算無向邊時 \( \delta(v) = \deg^-(v) - \deg^+(v) \)。
如果我們把一個邊定成 \( u \to v \) 後, \( \delta(u) \gets \delta(u) - 1, \delta(v) \gets \delta(v) + 1 \)。
可以想作一單位的 $\delta$ 從 \( u \) 流到了 \( v \)。
目標: delta(v) = 0
對於那些 \( \delta(v) > 0 \) 的,也就是需要流出 \( \delta(v) \) 的,我們就建 \( s \to v \)。
對於那些 \( \delta(v) < 0 \) 的,同理建 \( v \to t \)。
可以補好補滿 \( \iff \abs{f} = \sum_{\delta(v) > 0} \delta(v) \)
問題常常是一體兩面某個求最大值的問題常常等價於另一個求最小值的問題
第 3. 就是要找最省錢的一種生活方式
那最大流的對偶是什麼呢?
想像一下…
一個網路的最大流,就是那些被 「堵住」的水管的淨流量。
這些堵住的水管會把點分成兩群。
畫個圖
重點
而最大流的對偶問題正是最小割。
他們大小相同注意到有多組解, 3, 1 換 v5->t 也行
(1) \( \implies \) (2) 前面講過。
如果有路徑 可以增加流量。反方向沒有證
找所有在剩餘網路上, \( s \) 連的到的點,令其為 \( S \), 其他點就是 \( T \)。
\( t \) 不在 \(S\) 中。
這個 cut 上的邊都要流滿,否則有路徑。
\( C \leq \abs{f} \)
由截流知 \( \abs{f} \leq C \)
\( \abs{f} = C \)
(3) \( \implies \) (1) 也證完了。
這個引理也告訴了我們,找所有在剩餘網路上, \( s \) 連的到的點,令其為 \( S \), 其他點就為 \( T \),就恰好是一個 s-t cut。
裸的最小割,BJ4
先看個例題
分成要買/不要買的機器,要生產/不要生產的產品。
把要的就分到 \( S \),不要的就分到 \( T \)。
當然,還是要建模使得求出的答案即是題目要的。
回到那一題
和最大流時一樣,我們要求一個流量最大的流。
但現在每個邊除了有流量上限以外,還有一個價格 \( k(u, v) \), 表示每單位流過要付多少錢。
最後總花費是 \[ k(f) = \sum_{f(u, v) > 0} k(u, v) \cdot f(u, v) \]
如何修改前面的使得可以找一個最小花費的最大流?
我們先想剩餘網路要怎麼做修改。
流量應該不變。
那剩餘網路的花費 \( k_{f} (u, v) \) 如何設?
如果在原圖有一條有向邊 \( u \to v \) , Cost 為 \( k(u, v) \)。
k_f 是剩餘圖上的邊, k 是原來圖上的,不要搞混
一個負環就是花費總合是負的。有負環,加上去,更小!
由這兩個引理我們可以知道只要每次都找最短路擴充,直到最大流即可。
只是現在「最短路」並非邊數最少的路徑,而是花費總合最少的路徑!
綠色是 cost
int f = 0 while (tf = find_mincost_path()) { f += tf; } return f;
可以用任何能處理負邊的演算法,如 SPFA。
總複雜度是 \( \ord{SP \cdot \abs{f}} \)。
把一個機器人的工作歷程想做是一個流。
很多最大流的題目都可以推擴成花費流。
講一下導出子圖,畫個圖舉例
二分搜答案,假設最大密度為 \( k \)。
\[ \abs{E'} - k \abs{V'} \geq 0 \iff \max_{H = (V', E')} \abs{E'} - k \abs{V'} \geq 0 \]
\[ \abs{E'} = \frac{1}{2} \sum_{v \in V'} \deg_H(v) \]
\[ \deg_H(v) = \deg_G(v) - \sum_{\substack{u \notin H \\ (v, u) \in E}} 1 \]
\[ \Leftrightarrow \; - \min_{H = (V', E')} \sum_{v \in V'} \left( \left(2k - \deg_G(v) \right) + \sum_{\substack{u \notin H \\ (v, u) \in E}} 1 \right) \geq 0 \]
分兩類問題!
cost : 2k - deg sum 1 = 選 v 不選 u 就 1 cost
如果有一個路徑 \( v_1 \to v_2 \to v_3 \cdots \)
就是 \( \texttt{next}(v_1) = v_2, \texttt{next}(v_2) = v_3 \)匹配!
不如先補滿下界。
但有些點的流量就不守恆了,進來的跟出來的差了 \( \delta(v) \)。
有沒有想到混合圖的歐拉回路那一題!
\( M(G) = C_v(G) \)
把點覆蓋轉成 min-cut !
很不一樣的是這次在 s 表示 (v in X and 不選 v) or (v in Y and 選 v)
從構造的思路開始,要 u -> v 不能同時不選 = 不選 v 就要選 u