網路流
最大流知識點梳理
- 流網路,不考慮反向邊,可以存在環,有向圖。
- 可行流,不考慮反向邊。
(1)兩個條件:容量限制,流量守恆。
(2)可行流的流量指從源點流出的流量-流入源點的流量。
(3)最大流是指最大可行流。 - 殘留網路,考慮反向邊,和可行流一一對應,是原圖中邊數的二倍。其中正向的邊表示可以增加的流量,反向的邊表示可以退回去的流量。
殘留網路的可行流f’+原圖的可行流f=原題的另一個可行流。
(1)|f’+f| = |f|+|f’|
(2)|f’|可能是負數
- 增廣路徑,在殘留網路中,從源點出發,沿著容量大於0的邊走,如果能走到終點,則為增廣路徑。
- 割
(1)割的定義,把所有點分為兩個點集S、T,S包含源點,T包含匯點,兩個點集對於所有點來說補充不漏。
(2)割的容量,不考慮反向邊,最小割指的是容量最小的個割。
(3)割的流量,考慮反向邊f(S,T) <= c(S,T)。
(4)對於任意可行流f,任意割[S,T],|f| = f(S,T)。
(5)對於任意可行流f,任意割[S,T],|f| <= c(S,T)。
(6)最大流最小割定理:①可行流f是最大流②可行流的殘留網路不存在增廣路③存在某個割[S,T],|f| = c(S,T)
- 演算法
(1)EK O(nm^2)
AcWing 2171. EK求最大流
#include <bits/stdc++.h>
using namespace std;
int n, m, S, T;
const int N = 1010, M = 20010;
int h[N], e[M], ne[M], f[M], idx;
bool vis[N];
int pre[N], dis[N];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], f[idx] = c, h[a] = idx++;
e[idx] = a, ne[idx] = h[b], f[idx] = 0, h[b] = idx++;
}
bool bfs() {
memset(vis, false, sizeof vis);
queue<int> q;
q.push(S);
vis[S] = 1, dis[S] = 1e8;
while (q.size()) {
int t = q.front();
q.pop();
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (!vis[j] && f[i]) {
dis[j] = min(dis[t], f[i]);
vis[j] = 1, pre[j] = i;
q.push(j);
if (j == T) return true;
}
}
}
return false;
}
void EK() {
int ans = 0;
while (bfs()) {
ans += dis[T];
for (int i = T; i != S; i = e[pre[i] ^ 1])
f[pre[i]] -= dis[T], f[pre[i] ^ 1] += dis[T];
}
cout << ans << endl;
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m >> S >> T;
for (int i = 1; i <= m; i++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
EK();
return 0;
}