最小割等於最大流——這個定理讓「找瓶頸」和「找最大輸送量」是同一個問題。
問題的本質
一個管道網路,每條管道有容量限制。從水源 s 到終點 t,最多能送多少水?
暴力找路徑送水——找到一條 s→t 的路徑,沿這條路盡量送水。問題是貪婪選路可能堵死後面更好的方案。
殘餘圖(Residual Graph) 是解法的核心:
對容量 c、已流量 f 的邊 (u→v):
正向邊:剩餘容量 = c - f(還能送多少)
反向邊:容量 = f(允許「退回」之前送出的流量)
反向邊的意義是讓演算法能撤銷錯誤的選擇——等同於「取消這條路上的流,改送其他路徑」。有了殘餘圖,每次找到增廣路徑後更新,直到找不到為止。
Edmonds-Karp:BFS 增廣
Ford-Fulkerson 框架加上 BFS(確保每次找最短增廣路):
int edmondsKarp(int s, int t) {
int maxFlow = 0;
while (true) {
int[] parent = bfs(s, t); // BFS 找增廣路徑
if (parent[t] == -1) break;
int flow = Integer.MAX_VALUE;
for (int v = t; v != s; v = parent[v])
flow = Math.min(flow, residual(parent[v], v));
for (int v = t; v != s; v = parent[v]) {
reduce(parent[v], v, flow); // 正向邊減少剩餘容量
increase(v, parent[v], flow); // 反向邊增加剩餘容量
}
maxFlow += flow;
}
return maxFlow;
}時間複雜度:O(VE²)。
Dinic:分層圖 + 阻塞流
競程標準選擇,比 Edmonds-Karp 快很多:
兩步驟反覆執行到 t 不可達:
1. BFS 建分層圖(把節點按離 s 的 BFS 距離分層)
2. DFS 在分層圖中找阻塞流(一次 DFS 推進多條增廣路)
時間複雜度:O(V² E)
單位容量圖(競程常見):O(E √V)
Dinic 快的原因:每輪 BFS 後做完整的阻塞流,一次推進多條路徑,不像 Edmonds-Karp 每輪只推一條。
最大二分圖匹配
工作分配問題:m 個工人、n 個工作,每個工人能做某些工作,最多能成功分配幾對?
建圖:
超級源點 s → 每個工人(容量 1)
每個工作 → 超級匯點 t(容量 1)
工人 i → 工作 j(若 i 能做 j,容量 1)
最大流 = 最大匹配數
為什麼:每個工人最多被選一次(容量 1),每個工作最多被分配一次(容量 1)
最大流 = 最小割
最大流最小割定理:圖中從 s 到 t 的最大流 = 將 s 和 t 分開所需移除的最小邊容量總和。
最小割回答「哪裡是瓶頸」——供應鏈分析(哪條路徑最容易中斷)、網路安全(切斷哪些連接能隔離攻擊)。
殘餘圖的反向邊是個反直覺的設計——它讓演算法有能力承認錯誤並改正,這才是為什麼最大流能找到全局最優解。