网络流
我来透彻网络流了。因为懒得打字参考了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;
}