定义
网络:一张带权有向图,可以类比为输水系统
源点:可以提供无穷大流量的出发点,可以类比为大水库
源点要到达汇点,要经过许多中转点,中转点不存储流量,流入和与流出和相等。每条边的流量不允许超过该边的权值,这样限定了最大流不允许为
题目链接:P3376 【模板】网络最大流
算法概述
先来看这么一张图,源点是S,目的地为T
我们随机选则一条路,如:S->1->2->T,但显然最大流为1(S->1->T与S->2->T)
对于这种情况,可以考虑“撤销” 1 到 2 的操作,建一条大小为流量的反向边,换一条路,使总贡献增加
增广路:从源点开始,顺着有流量的边,能到达汇点,会使网络总流量增加的路
而求解最大流的算法就是不断寻找增广路,直到无法再增加为止。
考虑用爆搜寻找增广路(FF算法),显然会炸,需要优化
Dinic 算法
- 每次多路增广,若u点到v点的流量没有用完,可以考虑把剩下的流量贡献给其它u指向的点
- bfs分层,考虑回溯时可能会出现绕远或绕回,按照与源点的距离将图分层(bfs实现)
AC code
#include<bits/stdc++.h>
#define INF 0x7fffffff/3
#define reg register
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
#define IN freopen(".in.txt","r",stdin)
#define OUT freopen(".out.txt","w",stdout)
#define LL long long
using namespace std;
const int N=5e3+10;
int n,m,s,t,cnt=1,head[N];
queue<int> q;
int dep[N];
struct EDGE{
int pre,to;
long long val;
}edge[N<<1];
inline long long read(){
long long k=1,x=0;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') k=-1; ch=getchar();}
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-48,ch=getchar();
return k*x;
}
inline void add(int u,int v,long long w){
edge[++cnt]=(EDGE){head[u],v,w};
head[u]=cnt;
}
inline bool bfs(){ //寻找是否还有增广路
memset(dep,0,sizeof(dep));
dep[s]=1,q.push(s);
while (!q.empty()){
int u=q.front(); q.pop();
for (int i=head[u];i;i=edge[i].pre){
int v=edge[i].to;
if (edge[i].val&&!dep[v]) dep[v]=dep[u]+1,q.push(v); //搜还可以贡献流量的边
}
}
return dep[t]; //若 t 还可以被更新,说明还有增广路
}
inline long long dfs(int u,long long flow){
if (u==t) return flow;
long long dout=0;
for (int i=head[u];i&&flow;i=edge[i].pre){
int v=edge[i].to;
if (edge[i].val&&dep[v]==dep[u]+1){//限制只能流向下一层
long long res=dfs(v,min(flow,edge[i].val));
edge[i].val-=res;
edge[i^1].val+=res; //更新反向边的权值
flow-=res;
dout+=res;
}
}
if (!dout) dep[u]=0; //与汇点不连通,打上标记,之后不再更新
return dout;
}
int main(){
n=read(),m=read(),s=read(),t=read();
for (long long i=1,u,v,w;i<=m;i++) u=read(),v=read(),w=read(),add(u,v,w),add(v,u,0);
long long ans=0;
while (bfs()) ans+=dfs(s,1e18); //源点流量无限大
printf("%lld",ans);
return 0;
}