网络流
网络流 (network-flows) 是一种类比水流的解决问题方法,与线性规划密切相关。图论中的一种理论与方法,研究网络上的一类最优化问题。
网络流的图可以抽象成以下模型。
对于一张边权图,每条边的权值指流量。流量 是指允许通过该边的最大权,而可以有不限多的权同时停留在一点上。
特别地,一图中存在两特殊点:源点和汇点。
源点 指初状态拥有无穷多权的点。一般题目不提供这个点(告诉你就等于告诉你是网络流了),它是一个虚点。
汇点 指出度为
0
0
0 的、用于记录答案的虚点。
如上图所示,
S
T
ST
ST 是源点,
E
D
ED
ED 是汇点。
求从源点通过边走到汇点的权的最大值。这就是 最大流 问题。你可以把它想象成:有一些水管,它们有一定的耐久度,流过一定量的水后就会断裂失效。从一个有无穷多水的点开始流,最多有多少水流到汇点。
使用 Dinic 算法求最大流步骤如下。
- 把源点视为第一层,尝试遍历所有点并求出深度;
- 尝试 从浅到深 的顺序 从源点开始 流;
- 重复 1. 2. 操作。若无法搜到汇点,结束算法。
例题
求下图的最大流。
其中
inf
\inf
inf 表示无穷大。
我们首先从
S
T
ST
ST 分别流
inf
\inf
inf 到
1
,
2
,
3
1,2,3
1,2,3。尝试
1
→
5
,
6
;
5
,
6
→
E
D
1\to5,6;\ 5,6\to ED
1→5,6; 5,6→ED,对答案贡献为
2
2
2;
2
→
5
,
t
→
E
D
,
a
n
s
+
5
2\to5,t\to ED,ans+5
2→5,t→ED,ans+5;
3
→
4
,
4
→
E
D
,
a
n
s
+
1
3\to4,4\to ED,ans+1
3→4,4→ED,ans+1。
所以最大流为
2
+
5
+
1
=
8
2+5+1=8
2+5+1=8。
当题目描述与“流水”无关时,尝试将题目转换成流水模型。
luogu P2740 [USACO4.2]草地排水Drainage Ditches 农夫约翰知道每一条排水沟每分钟可以流过的水量,和排水系统的准确布局(起点为水潭而终点为小溪的一张网)。需要注意的是,有些时候从一处到另一处不只有一条排水沟。
根据这些信息,计算从水潭排水到小溪的最大流量。对于给出的每条排水沟,雨水只能沿着一个方向流动,注意可能会出现雨水环形流动的情形。
参考代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=210;
const int inf=0x3f3f3f3f;
struct edge{
int x,y,d,next,other;
}e[MAXN+MAXN];
int len=0;
int first[MAXN];
int n,m;
int sx,sy,sd;
int f[MAXN],q[MAXN],h[MAXN];
int st,ed;
void ins(int x,int y,int d){
e[++len].x=x;e[len].y=y;e[len].d=d;
e[len].next=first[x];first[x]=len;
e[++len].x=y;e[len].y=x;e[len].d=0;
e[len].next=first[y];first[y]=len;
e[len].other=len-1;
e[len-1].other=len;
}
int bfs(){
memset(h,0,sizeof(h));h[1]=1;
st=1;ed=2;q[1]=1;
while(st!=ed){
int x=q[st];
for(int i=first[x];i;i=e[i].next){
int y=e[i].y;
if(!h[y]&&e[i].d>0){
h[y]=h[x]+1;
q[ed++]=y;
}
}
++st;
}
return bool(h[n]>0);
}
int dfs(int x,int f){
if(x==n) return f;
int tt=0;
for(int i=first[x];i;i=e[i].next){
int y=e[i].y;
if(h[y]==h[x]+1&&e[i].d>0&&f>=tt){
int flow=dfs(y,min(f-tt,e[i].d));
tt+=flow;
e[i].d-=flow;
e[e[i].other].d+=flow;
}
}
if(!tt) h[x]=0;
return tt;
}
inline int read(){
int x=0; char c;
do c=getchar(); while(c<'0'||c>'9');
while(c>='0'&&c<='9')
x=x*10+c-48,c=getchar();
return x;
}
int main(){
m=read();n=read();
memset(first,0,sizeof(first));
for(int i=1;i<=m;++i){
sx=read();sy=read();sd=read();
ins(sx,sy,sd);
}
int ans=0;
while(bfs())
ans+=dfs(1,inf);
printf("%d",ans);
}
网络流 24 题索引
此表按笔者解决的时间顺序排列,一定程度上反映了难度情况。可供参考。
序号 | 题目 | 题解 |
---|---|---|
1 1 1 | P2756 飞行员配对方案问题 | 已解决 |
2 2 2 | P4016 负载平衡问题 | 已解决 |
3 3 3 | P2763 试题库问题 | 已解决 |
4 4 4 | P2761 软件补丁问题 | 未解决 |