我來透徹網絡流了。因為懶得打字參考了LNOI創始人的博客qwq。說白了就是粘
網絡流
網絡流定義
在一個有向圖上選擇一個源點,一個匯點,每一條邊上都有一個流量上限(以下稱為容量),即經過這條邊的流量不能超過這個上界,同時,除源點和匯點外,所有點的入流和出流都相等,而源點只有流出的流,匯點只有匯入的流。
這樣的圖叫做網絡流。
網絡流相關定義
-
源點:有\(n\)個點,有\(m\)條有向邊,有一個點很特殊,只出不進,叫做源點。
-
匯點:另一個點也很特殊,只進不出,叫做匯點。
-
容量和流量:每條有向邊上有兩個量,容量和流量,從i到j的容量通常用\(c[i,j]\)表示,流量則通常是\(f[i,j]\)。
通常可以把這些邊想象成道路,流量就是這條道路的車流量,容量就是道路可承受的最大的車流量。
很顯然的,流量\(\leq\)容量。而對於每個不是源點和匯點的點來說,可以類比的想象成沒有存儲功能的貨物的中轉站,所有“進入”他們的流量和等於所有從他本身“出去”的流量。 -
最大流:把源點比作工廠的話,問題就是求從工廠最大可以發出多少貨物,是不至於超過道路的容量限制,也就是,最大流。
增廣路算法
該方法通過尋找增廣路來更新最大流,有\(EK,dinic,SAP,ISAP\)主流算法。
最常用的就是\(dinic\)。
但是費用流需要用到\(EK\),所以要學會\(EK\),\(dinic\)算法;
增廣路:在圖中若經過一條從源點到匯點的路徑后,路徑上所有邊的剩余容量都\(>0\)(注意是\(>\)不是\(\geq\)),那么這條路徑被稱為增廣路。
我們通過找到增廣路來求解最大流,因為只要有增廣路,你的所求最大流就是不最大的,當無增廣路之后,最大流就求出來了讀者自證不難。
dinic
#include <bits/stdc++.h>
using namespace std;
const int maxn(210), maxm(5010);
int n, m, s, t, cnt = 1, head[maxn], dep[maxn], cur[maxn];
long long ans;
struct edge{
int x, y, z, nxt;
}e[maxm<<1];
inline void add_edge(int x, int y, int z){
++cnt;
e[cnt].x = x, e[cnt].y = y, e[cnt].z = z;
e[cnt].nxt = head[x];
head[x] = cnt;
return;
}
bool bfs(){
for(int i = 1; i <= n; i ++) dep[i] = 0;
queue<int> q;
q.push(s);
dep[s] = 1;
while(q.size()){
int now = q.front();
q.pop();
for(int i = head[now]; i; i = e[i].nxt){
int to = e[i].y, len = e[i].z;
if(len and !dep[to]){
dep[to] = dep[now] + 1;
q.push(to);
if(to == t) return 1;
}
}
}
return 0;
}
int dfs(int now, int num){
if(now == t) return num;
int res = num;
for(int i = cur[now]; i and res; i = e[i].nxt){
cur[now] = i;
int to = e[i].y, len = e[i].z;
if(len and (dep[to] == dep[now] + 1)){
int k = dfs(to, min(res, len));
if(!k) dep[to] = 0;
e[i].z -= k, e[i^1].z += k, res -= k;
}
}
return num-res;
}
void dinic(long long &ans){
while(bfs()){
for(int i = 1; i <= n; i ++) cur[i] = head[i];
int num = 0;
while(num = dfs(s,1<<29)) ans += num;
}
return;
}
signed main(){
scanf("%d%d%d%d", &n, &m, &s, &t);
for(int x, y, z; m; m --){
scanf("%d%d%d", &x, &y, &z);
add_edge(x,y,z);
add_edge(y,x,0);
}
dinic(ans);
printf("%lld", ans);
return 0;
}