假目录

·网络流简介

·最大流

  EK算法

·最小割

·Dinic

  当前弧优化

  最大流标程

·费用流

  正确性证明

  dijkstra费用流

  标程

·上下界最大流

 

 

 

网络流简介

有一个有向图,每条边为一条管道,有固定的容量

从一个源点出发,它可以提供无限的水流,

流到一个汇点,它可以接受无限的水流,

每条边可以有一些水流通过,水流的大小不能超过容量,

我们把一个合法的解叫做一个,一条边的水流大小叫做流量。

定义一条边的残量,是指它还能流多少流量(即容量减去当前流量)

性质:

1.容量限制:每条边的流量不超过其容量。

2.流量平衡:除源点和汇点外,对于每个点,流入它的流量和等于从它流出的流量和。

最大流:就是最大的流

最大化整个流的流量 ⇒ 最大化从源点流出的流量。

 

 

求解最大流

一个贪心的思路:

每次从残量网络中任意找一条到达汇点路径,直到没有可以到达汇点的路径

然而它是错的

网络流问题略解

 

显然如果走了红色的路径,就不能再走其他的路径了

然而最优解是走上下两条路径。。

那么我们考虑“反悔操作”

我们想让水流“回去”,从而找到更优的解

一个可行的方法是建反向边

每次进行一次增流后,将正向边残联-流量,反向边容量+流量

显然,在反向边上增流,就相当于正向边上的流量减少,而且

总的残量是在不断减小的


上面的例子在找到1->2->3->4的路径后,图变为了这样:

网络流问题略解

那么我们就又有了1->3->2->4一条路径,对它进行增广

于是就有了2的流量

这就是FF方法

1.在残量网络上找到一条从源点到汇点的道路(称为“增广路”)

2.取增广路上残量最小值v

3.将答案加上v

4.将增广路上所有边的残量减去v ,它们的反向边的残量加上 v。

由于残量网络不断在减小,它是一定会结束的

正确性之后会有证明

于是我们就可以为所欲为了

 

然而有时它会被卡

网络流问题略解

 

若走了1->2->3->4,

网络流问题略解

于是变成了这样

 网络流问题略解

(明显抠图痕迹)

于是又有了一条1->3->2->4的路径

然后。。就被卡到了1100000000次

如何避免这种情况?

我们看一下EK算法:

——每次寻找最短增广路增广

每次增广的道路长度显然是不下降的,

所以不会被上面的极端情况卡掉

寻找最短路BFS即可 

其复杂度是 O(m^2 * n)的

网络流问题略解

 

 

最小割

选出一些边的集合,

使得删除它们之后从源点无法到达汇点,

那么这个集合就叫做一个

这些边的容量之和称作这个割的容量。

最小割就是一个容量最小的割边集合

显然 ,任取一个割,其割的容量大于最大流的流量

最小割的容量等于最大流的流量,且FF方法能够正确的求出它

考虑 FF 算法结束时,残量网络上没有了增广路。

那么我们假设这时候,从源点经过残量网络能到达的点组成的集合为 X , 不能到达的点为 Y 。

显然汇点在 Y 里,并且残量网络上没有从 X 到 Y 的边。

可以发现以下事实成立:

1.Y 到 X 的边流量为 0 。如果流量不为 0 那么应该存在一条从 X 到 Y 的反向边,于是矛盾。

2.X 到 Y 的边流量等于其容量。只有这样它才不会在残量网络中出现。

根据第1个条件,我们可以得知:没有流量从 X 到 Y 之后又回到了 X 。

所以当前流量应该等于从 X 到 Y 的边的流量之和;

根据第2个条件,它又等于从 X 到 Y 的边容量之和。

所以FF方法也证明了是正确的。。

 

既然已经证明van了,我们进入下一环节

 

 

Dinic

EK算法每增广一条道路都要进行一次bfs,显然是比较慢的

考虑EK算法的优化:若增广后源点到汇点的最短路长度不变,不需要再bfs,直到最短路的长度变大再bfs

一次bfs找多个增广路径

增广时用dfs,记录下当前路径能允许的最大的流量,到达汇点时就统计

 

网络流问题略解

 

前弧优化:

若在一次dfs中一些边已经访问过,在本次增广中就不会再产生新的贡献

于是我们直接跳过访问过的边

对于每个点维护一个“当前弧”cur[x]

表示从它开始的第一个可能可以增广的边是谁

for循环访问后将cur[x]置为最后的边i

每次for循环从cur[x]开始,而不是head[x]

bfs时将cur[x]置为head[x]

网络流问题略解

 

 模板最大流Dinic+当前弧优化完整代码:

 

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 
 5 #define N 10010
 6 #define M 200010
 7 #define INF 0x3f3f3f3f
 8 #define min(a,b) (a<b?a:b)
 9 
10 const int ch_top=4e7+3;
11 char ch[ch_top],*now_r=ch-1;
12 
13 int n,m,s,t,ans;
14 
15 int Head[N],to[M],w[M],next[M],num=1;
16 
17 int deep[N],cur[N],que[N],head,tail;
18 
19 inline void add(int x,int y,int v){
20     to[++num]=y;
21     w[num]=v;
22     next[num]=Head[x];
23     Head[x]=num;
24     to[++num]=x;
25     w[num]=0;
26     next[num]=Head[y];
27     Head[y]=num;
28 }
29 
30 inline int read(){
31     while(*++now_r<'0');
32     register int x=*now_r-'0';
33     while(*++now_r>='0')x=x*10+*now_r-'0';
34     return x;
35 }
36 
37 bool bfs(){
38     memset(deep,-1,sizeof(deep));
39     head=tail=0;
40     deep[s]=0;
41     que[++tail]=s;
42     while(head<tail){
43         int u=que[++head]; cur[u]=Head[u];
44         for(int i=Head[u];i;i=next[i]){
45             int v=to[i];
46             if(deep[v]!=-1||!w[i]) continue;
47             deep[v]=deep[u]+1;
48             que[++tail]=v;
49             if(v==t) return 1;
50         }
51     }
52     return 0;
53 }
54 
55 int dfs(int now,int cap){
56     if(now==t||!cap) return cap;
57     int flow=0,f;
58     for(int i=cur[now];i;cur[now]=i=next[i]){
59         int v=to[i];
60         if(!w[i]||deep[v]!=deep[now]+1) continue;
61         if(!(f=dfs(v,min(cap,w[i])))) continue;
62         w[i]-=f;
63         cap-=f;
64         w[i^1]+=f;
65         flow+=f;
66         if(!cap) break;
67     }
68     return flow;
69 }
70 
71 void Dinic(){
72     while(bfs())
73      ans+=dfs(s,INF);
74 }
75 
76 int main()
77 {
78     fread(ch,1,ch_top,stdin);
79     n=read(); m=read(); s=read(); t=read();
80     int x,y,v;
81     for(int i=1;i<=m;i++){
82         x=read(); y=read(); v=read();
83         add(x,y,v);
84     }
85     Dinic();
86     printf("%d\n",ans);
87     return 0;
88 } 
View Code

相关文章: