【模板】网络最大流
题目描述
如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。
输入格式
第一行包含四个正整数 n , m , s , t n,m,s,t n,m,s,t,分别表示点的个数、有向边的个数、源点序号、汇点序号。
接下来 m m m 行每行包含三个正整数 u i , v i , w i u_i,v_i,w_i ui,vi,wi,表示第 i i i 条有向边从 u i u_i ui 出发,到达 v i v_i vi,边权为 w i w_i wi(即该边最大流量为 w i w_i wi)。
输出格式
一行,包含一个正整数,即为该网络的最大流。
样例 #1
样例输入 #1
4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 30
样例输出 #1
50
提示
样例输入输出 1 解释
题目中存在 3 3 3 条路径:
- 4 → 2 → 3 4\to 2\to 3 4→2→3,该路线可通过 20 20 20 的流量。
- 4 → 3 4\to 3 4→3,可通过 20 20 20 的流量。
- 4 → 2 → 1 → 3 4\to 2\to 1\to 3 4→2→1→3,可通过 10 10 10 的流量(边 4 → 2 4\to 2 4→2 之前已经耗费了 20 20 20 的流量)。
故流量总计 20 + 20 + 10 = 50 20+20+10=50 20+20+10=50。输出 50 50 50。
数据规模与约定
- 对于 30 % 30\% 30% 的数据,保证 n ≤ 10 n\leq10 n≤10, m ≤ 25 m\leq25 m≤25。
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 200 1 \leq n\leq200 1≤n≤200, 1 ≤ m ≤ 5000 1 \leq m\leq 5000 1≤m≤5000, 0 ≤ w < 2 31 0 \leq w\lt 2^{31} 0≤w<231。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll INF=1e9;
int n,m,s,t;
const int N=250, M=11000; //M定义为边的两倍:双向边
int cnt=1,head[N]; //8-16行:链式前向星。cnt初值不能是0,可以是1、3、5
struct {int to, nex, w;} e[M];
void add(int u,int v,int w) {
cnt++; //cnt初值是1,cnt++后第一个存储位置是cnt=2,是偶数位置
e[cnt].to = v;
e[cnt].w = w;
e[cnt].nex = head[u];
head[u] = cnt;
}
int now[N],dep[N]; //dep[]记录点所在的层次(深度)
int bfs() { //在残留网络中构造分层图
for(int i=1;i<=n;i++) dep[i]=INF;
dep[s] = 0; //从起点s开始分层
now[s] = head[s]; //当前弧优化。now是head的拷贝
queue<int>Q; Q.push(s);
while(!Q.empty()) {
int u = Q.front(); Q.pop();
for(int i=head[u]; i>0;i=e[i].nex) { //搜点u的所有邻居,邻居是下一层
int v = e[i].to;
if(e[i].w>0 && dep[v]==INF) { // e[i].w>0表示还有容量
Q.push(v);
now[v] = head[v];
dep[v] = dep[u]+1; //分层:u的邻居v是u的下一层
if(v==t) return 1; //搜到了终点,返回1
}
}
}
return 0; //如果通过有剩余容量的边无法到达终点t,即t不在残留网络中,返回0
}
int dfs(int u,ll sum) { //sum是这条增广路对最大流的贡献
if(u==t) return sum;
ll k,flow=0; //k是当前最小的剩余容量
for(int i=now[u]; i>0 && sum>0; i=e[i].nex) {
now[u]=i; //当前弧优化
int v=e[i].to;
if(e[i].w>0 && (dep[v]==dep[u]+1)){ //分层:用dep限制只能访问下一层
k = dfs(v,min(sum,(ll)e[i].w));
if(k==0) dep[v] = INF; //剪枝,去掉增广完毕的点。其实把INF写成0也行
e[i].w -= k; //更新残留网络:正向减
e[i^1].w += k; //更新残留网络:反向加。小技巧:奇偶边
flow += k; //flow表示经过该点的所有流量和
sum -= k; //sum表示经过该点的剩余流量
}
}
return flow;
}
int main() {
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++) {
int u,v,w; scanf("%d%d%lld",&u,&v,&w);
add(u,v,w); add(v,u,0); //双向边,反向边的容量是0
}
ll ans=0;
while(bfs()) ans += dfs(s,INF); //先后做BFS和DFS。当t不在残留网络中时退出
printf("%lld",ans);
return 0;
}