【题目来源】
https://www.luogu.com.cn/problem/P3376
https://www.acwing.com/problem/content/2174/
【题目描述】
如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。
【输入格式】
第一行包含四个正整数 n,m,s,t,分别表示点的个数、有向边的个数、源点序号、汇点序号。
接下来 m 行每行包含三个正整数 ui,vi,wi,表示第 i 条有向边从 ui 出发,到达 vi,边权为 wi(即该边最大流量为 wi)。
【输出格式】
一行,包含一个正整数,即为该网络的最大流。
【输入样例】
4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 30
【输出样例】
50
【数据范围】
对于 30% 的数据,保证 n≤10,m≤25。
对于 100% 的数据,保证 1≤n≤200,1≤m≤5000,0≤w<2^31。
【算法分析】
● Dinic 算法被认为是网络流问题的"瑞士军刀"。在算法竞赛和面试中,掌握了 Dinic 算法就解决了 90% 的最大流问题。
● 网络流常用示意图(https://blog.csdn.net/hnjzsyjyj/article/details/139595477)
在残量网络中找可增广路;
在实流网络中沿可增广路增流,在残量网络中沿可增广路减流。

增广路定理:设 flow 是网络 G 的一个可行流,如果不存在从源点 s 到汇点 t 关于 flow 的可增广路p,则 flow 是 G 的一个最大流。
● 残量网络(Residual Network)是网络流算法的核心概念。它不是独立构建的数据结构,而是基于原网络和当前流量动态计算出的"可用容量"的表示。
对于原网络 G=(V,E) 中的每条边 (u,v),其容量为 c(u,v),当前流量为 f(u,v)。残量网络包含两类边:
(1)正向边:剩余容量 c(u,v)−f(u,v)
(2)反向边:剩余容量 f(u,v)
● 利用"^1"运算表示反向边
由于网络流是有向有权图,因此可以选择链式前向星(https://blog.csdn.net/hnjzsyjyj/article/details/139369904)存图。对一个数连续执行两次"^1"运算后,便会得到自身。这恰好与网络流中"反向边的反向边等于自身"不谋而合。因此,在网络流的算法实现中,我们可以利用"^1"运算来表示反向边。
● Dinic 算法中不需要显式构建完整的残量网络,而是通过修改边的容量来隐式维护。
● Dinic 不是简单的 DFS,而是"BFS 分层 + DFS 多路增广"的完美结合。其核心流程如下:
(1)BFS 构建分层图:从源点出发,计算每个节点到源点的最短距离(边数),形成"层次"。在残量网络 中,规定只能从低层走向高层(d[v] = d[u] + 1),这有效避免了 DFS 在环里绕圈子。
(2)DFS 多路增广:在分层图上进行 DFS,一次性找出当前层次限制下所有可能的增广路,并更新流量。
(3)重复:当 BFS 无法到达汇点时,算法结束。
● 在 Dinic 算法中,用 STL 的 std::queue 写 BFS 层级图构建,确实很容易在性能敏感的大规模图(特别是稠密图、多次 BFS)中成为性能瓶颈,甚至导致 TLE。
【算法代码】
(1)本题代码中,dinic() 函数的返回值为 long long 型。
(2)由于 dinic() 函数需要建反向边,所以代码中的边数,至少需要设为题目陈述中边数的两倍。
(3)f[i] 表示第 i 条边的「剩余容量」。不是总流量,不是已用流量,是还能流多少!初始时,f[i] = 边的容量。流完后,f[i] 减少,f[i^1] 增加(反悔用)。
cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e4+5,M=2e5+5;
const int INF=INT_MAX;
int n,m,S,T;
int h[N],e[M],ne[M],f[M],idx;
int d[N],cur[N],q[N];
void add(int a,int b,int w) {
f[idx]=w,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
f[idx]=0,e[idx]=a,ne[idx]=h[b],h[b]=idx++;
}
bool bfs() {
memset(d,-1,sizeof d);
int hh=0,tt=0;
q[0]=S,d[S]=0;
while(hh<=tt) {
int u=q[hh++];
for(int i=h[u]; ~i; i=ne[i]) {
int v=e[i];
if(d[v]==-1 && f[i]) {
d[v]=d[u]+1;
q[++tt]=v;
}
}
}
return d[T]!=-1;
}
int dfs(int u,int lim) {
if(u==T) return lim;
int flow=0;
for(int i=cur[u]; ~i && flow<lim; i=ne[i]) {
cur[u]=i;
int v=e[i];
if(d[v]==d[u]+1 && f[i]) {
int t=dfs(v,min(f[i],lim-flow));
if(!t) d[v]=-1;
f[i]-=t,f[i^1]+=t,flow+=t;
}
}
return flow;
}
LL dinic() {
LL r=0,flow=0;
while(bfs()) {
for(int i=1; i<=n; i++) cur[i]=h[i];
while(flow=dfs(S,INF)) r+=flow;
}
return r;
}
int main() {
memset(h,-1,sizeof h);
cin>>n>>m>>S>>T;
while(m--) {
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
cout<<dinic()<<endl;
return 0;
}
/*
in:
4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 30
out:
50
*/
【参考文献】
https://www.acwing.com/video/1537/
https://www.acwing.com/solution/content/68463/
https://zhuanlan.zhihu.com/p/573015182
https://www.acwing.com/file_system/file/content/whole/index/content/6610796/
https://www.acwing.com/file_system/file/content/whole/index/content/6511769/
https://www.acwing.com/file_system/file/content/whole/index/content/10718893/
https://blog.csdn.net/hnjzsyjyj/article/details/139595477
https://www.luogu.com.cn/problem/solution/P3376
https://www.cnblogs.com/bianchengmao/p/16713542.html
https://www.acwing.com/file_system/file/content/whole/index/content/10718684/