网络流入门——Edmond-Karp算法

原文地址

一、概念引入

首先要先清楚最大流的含义,就是说从源点到经过的所有路径的最终到达汇点的所有流量和。

      流网络G=(V,E)是一个有向图,其中每条边(u,v)∈E均有一个非负容量c(u,v)>=0。如果(u,v)不属于E,则假定c(u,v)=0。流网络中有两个特别的顶点:源点s和汇点t。下图展示了一个流网络的实例(其中斜线左边的数字表示实际边上的流,右边的数字表示边的最大容量):

image

二、基础知识准备

      对一个流网络G=(V,E),其容量函数为c,源点和汇点分别为s和t。G的流f满足下列三个性质: 
      容量限制:对所有的u,v∈V,要求f(u,v)<=c(u,v)。 
      反对称性:对所有的u,v∈V,要求f(u,v)=-f(v,u)。 
      流守恒性:对所有u∈V-{s,t},要求∑f(u,v)=0 (v∈V)。

容量限制说明了从一个顶点到另一个顶点的网络流不能超过设定的容量,就好像是一个管道只能传输一定容量的水,而不可能超过管道体积的限制;反对称性说明了从顶点u到顶点v的流是其反向流求负所得,就好像是当参考方向固定后,站在不同的方向看,速度一正一负;而流守恒性说明了从非源点或非汇点的顶点出发的点网络流之和为0,这有点类似于基尔霍夫电流定律,且不管什么是基尔霍夫电流定律,通俗的讲就是进入一个顶点的流量等于从该顶点出去的流量,如果这个等式不成立,必定会在该顶点出现聚集或是枯竭的情况,而这种情况是不应该出现流网络中的,所以一般的最大流问题就是在不违背上述原则的基础上求出从源点s到汇点t的最大的流量值,显然这个流量值应该定义为从源点出发的总流量或是最后聚集到t的总流量,即流f的值定义为|f|=∑f(s,v) (v∈V)。

在求解最大流的问题前,必须对三个概念有所了解:残留网络,增广路径和割。下面先给出这三个概念的基本内容。 
      a.在给定的流网络G=(V,E)中,设f为G中的一个流,并考察一对顶点u,v∈V,在不超过容量c(u,v)的条件下,从u到v之间可以压入的额外网络流量,就是(u,v)的残留容量,就好像某一个管道的水还没有超过管道的上限,那么就这条管道而言,就一定还可以注入更多的水。残留容量的定义为:cf(u,v)=c(u,v)-f(u,v)。而由所有属于G的边的残留容量所构成的带权有向图就是G的残留网络。下图就是上面的流网络所对应的残留网络:

对比于:

image


image

残留网络中的边既可以是E里面的边,也可以是此边的反向边。只有当两条边(u,v)和(v,u)中,至少有一条边出现在初始网络中时,边(u,v)才会出现在残留网络中。下面是一个有关残留网络的定理,若f是G中的一个流,Gf是由G导出的残留网络,f'是Gf中的一个流,则f+f'是G中一个流,且其值|f+f'|=|f|+|f'|。证明时只要证明f+f'这个流在G中满足之前所讲述的三个原则即可。在这里只给出理解性的证明,可以想象如果在一个管道中流动的水的总流量为f,而在该管道剩余的流量中存在一个流f'可以满足不会超过管道剩余流量的最大限,那么将f和f'合并后,也必定不会超过管道的总流量,而合并后的总流量值也一定是|f|+|f'|。

b.增广路径p为残留网络Gf中从s到t的一条简单路径。根据残留网络的定义,在不违反容量限制的条件下,G中所对应的增广路径上的每条边(u,v)可以容纳从u到v的某额外正网络流。而能够在这条路径上的网络流的最大值一定是p中边的残留容量的最小值。这还是比较好理解的,因为如果p上的流大于某条边上的残留容量,必定会在这条边上出现流聚集的情况。所以我们将最大量为p的残留网络定义为:cf(p)=min{cf(u,v) | (u,v)在p上}。而结合之前在残留网络中定理,由于p一定是残留网络中从s到t的一条路径,且|f'|=cf(p),所以若已知G中的流f,则有|f|+|cf(p)|>|f|且|f|+|cf(p)|不会超过容量限制。

c.流网络G(V,E)的割(S,T)将V划分成为S和T=V-S两部分,使得s∈S,t∈T。如果f是一个流,则穿过割(S,T)的净流被定义为f(S,T)=∑f(x,y) (x∈S,y∈T),割(S,T)的容量为c(S,T)。一个网络的最小割就是网络中所有割中具有最小容量的割。设f为G中的一个流,且(S,T)是G中的一个割,则通过割(S,T)的净流f(S,T)=|f|。因为f(S,T)=f(S,V)-f(S,S)=f(S,V)=f(s,V)+f(S-s,V)=f(s,V)=|f|(这里的公式根据f(X,Y)=∑f(x,y) (x∈X,y∈Y)的定义,以及前面的三个限制应该还是可以推出来的,这里就不细讲了)。有了上面这个定理,我们可以知道当把流不断增大时,流f的值|f|不断的接近最小割的容量直到相等,如果这时可以再增大流f,则f必定会超过某个最小的割得容量,则就会存在一个f(S,T)<=c(S,T)<|f|,显然根据上面的定理这是不可能。所以最大流必定不超过网络最小割的容量。

综合上面所讲,有一个很重要的定理:最大流最小割定理

如果f是具有源s和汇点t的流网络G=(V,E)中的一个流,则下列条件是等价的: 
      1) f是G中一个最大流。 
      2) 残留网络Gf不包含增广路径。 
      3) 对G的某个割(S,T),有|f|=c(S,T)。


*********************************** 分割线 ***********************************

以下来自另一篇博客

转载:网络流基础篇——Edmond-Karp算法             BY纳米黑客

网络流的相关定义:

  • 源点:有n个点,有m条有向边,有一个点很特殊,只出不进,叫做源点
  • 汇点:另一个点也很特殊,只进不出,叫做汇点
  • 容量和流量:每条有向边上有两个量,容量和流量,从i到j的容量通常用c[i,j]表示,流量则通常是f[i,j].

通常可以把这些边想象成道路,流量就是这条道路的车流量,容量就是道路可承受的最大的车流量。很显然的,流量<=容量。而对于每个不是源点和汇点的点来说,可以类比的想象成没有存储功能的货物的中转站,所有“进入”他们的流量和等于所有从他本身“出去”的流量。

  • 最大流:把源点比作工厂的话,问题就是求从工厂最大可以发出多少货物,是不至于超过道路的容量限制,也就是,最大流

网络流基础篇——Edmond-Karp算法

求解思路:

首先,假如所有边上的流量都没有超过容量(不大于容量),那么就把这一组流量,或者说,这个流,称为一个可行流

一个最简单的例子就是,零流,即所有的流量都是0的流。

  • (1).我们就从这个零流开始考虑,假如有这么一条路,这条路从源点开始一直一段一段的连到了汇点,并且,这条路上的每一段都满足流量<容量,注意,是严格的<,而不是<=。
  • (2).那么,我们一定能找到这条路上的每一段的(容量-流量)的值当中的最小值delta。我们把这条路上每一段的流量都加上这个delta,一定可以保证这个流依然是可行流,这是显然的。
  • (3).这样我们就得到了一个更大的流,他的流量是之前的流量+delta,而这条路就叫做增广路。我们不断地从起点开始寻找增广路,每次都对其进行增广,直到源点和汇点不连通,也就是找不到增广路为止。
  • (4).当找不到增广路的时候,当前的流量就是最大流,这个结论非常重要。

补充:

  • (1).寻找增广路的时候我们可以简单的从源点开始做BFS,并不断修改这条路上的delta 量,直到找到源点或者找不到增广路。
  • (2).在程序实现的时候,我们通常只是用一个c 数组来记录容量,而不记录流量,当流量+delta 的时候,我们可以通过容量-delta 来实现,以方便程序的实现。

 

相关问题:

为什么要增加反向边?

在做增广路时可能会阻塞后面的增广路,或者说,做增广路本来是有个顺序才能找完最大流的。

但我们是任意找的,为了修正,就每次将流量加在了反向弧上,让后面的流能够进行自我调整。

举例:

比如说下面这个网络流模型

3

我们第一次找到了1-2-3-4这条增广路,这条路上的delta值显然是1。

于是我们修改后得到了下面这个流。(图中的数字是容量)

4

这时候(1,2)和(3,4)边上的流量都等于容量了,我们再也找不到其他的增广路了,当前的流量是1。

但是,

这个答案明显不是最大流,因为我们可以同时走1-2-4和1-3-4,这样可以得到流量为2的流。

那么我们刚刚的算法问题在哪里呢

问题就在于我们没有给程序一个“后悔”的机会,应该有一个不走(2-3-4)而改走(2-4)的机制。

那么如何解决这个问题呢

我们利用一个叫做反向边的概念来解决这个问题。即每条边(i,j)都有一条反向边(j,i),反向边也同样有它的容量。

我们直接来看它是如何解决的:

在第一次找到增广路之后,在把路上每一段的容量减少delta的同时,也把每一段上的反方向的容量增加delta。

           c[x,y]-=delta;
           c[y,x]+=delta;


我们来看刚才的例子,在找到1-2-3-4这条增广路之后,把容量修改成如下:

1

这时再找增广路的时候,就会找到1-3-2-4这条可增广量,即delta值为1的可增广路。将这条路增广之后,得到了最大流2。

2

那么,这么做为什么会是对的呢?

事实上,当我们第二次的增广路走3-2这条反向边的时候,就相当于把2-3这条正向边已经是用了的流量给“退”了回去,不走2-3这条路,而改走从2点出发的其他的路也就是2-4。

如果这里没有2-4怎么办?

这时假如没有2-4这条路的话,最终这条增广路也不会存在,因为他根本不能走到汇点

同时本来在3-4上的流量由1-3-4这条路来“接管”。而最终2-3这条路正向流量1,反向流量1,等于没有流。

个人的c++实现代码:

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
//capacity:容量
//flow:流量
//parent:记录在一条增广路中每个节点的前一个节点
//alpha:记录在增广路中当每个节点所能调整的流量的最大值
const int N = 20005;
int n,m,s,t;
struct Node{
    
    
    int v,ne,f;//f表示流量,及边的权值
}e[N];
int h[N],idx,vis[N],d[N];//d[i]表示
int pre[N];//pre[i]记录点i是从哪一条边转移过来的

//下标为i的边的反向边为i^1
void add(int a,int b,int c){
    
    
    e[idx].v = b,e[idx].f = c,e[idx].ne = h[a],h[a] = idx++;
    e[idx].v = a,e[idx].f = 0,e[idx].ne = h[b],h[b] = idx++;
}

//寻找增广路径
bool bfs(){
    
    
    int i,j;
    memset(vis,0,sizeof vis);
    queue<int> q;
    q.push(s);
    d[s] = inf;vis[s] = 1;
    while(!q.empty()){
    
    
        int x = q.front();q.pop();
        for(i = h[x];i != -1;i = e[i].ne){
    
    
            int v = e[i].v;
            //如果该点没有访问过,并且该边的容量大于0
            if(vis[v] == 0 && e[i].f>0){
    
    
                vis[v] = 1;
                //记录到达点v的最小边权
                d[v] = min(e[i].f,d[x]);
                //记录从哪条边过来的
                pre[v] = i;
                q.push(v);
                if(v == t)  return true;
            }
        }
    }
    return false;
}

//EK算法
int EK(){
    
    
    int i,j;
    int ans = 0;
    while(bfs()){
    
    
        //当存在增广路径的时候
        //正向边-=d[t],反向边+=d[t];
        ans+=d[t];
        for(i=t;i!=s;i=e[pre[i]^1].v){
    
    
            e[pre[i]].f -= d[t];
            e[pre[i]^1].f += d[t];
        }
    }
    return ans;
}

int main(){
    
    
    int i,j;
    memset(h,-1,sizeof h);
    cin>>n>>m>>s>>t;
    for(i = 1;i<=m;i++){
    
    
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    //求最大流
    cout<<EK()<<endl;
    return 0;
}

推荐博客

个人总结:
残留网络:
残留网络指的是在原网络中指定了可行流之后对应的一个网络流,原网络的一个可行流对应一个残留网络。

  • 残留网络的每条边c’(u,v) = c(u,v)-f(u,c);
  • 残留网络的每条反向边c’(v,u) = f(u,v);
  • 残留网络的可行流f’ + 原图的可行流f = 原题的另一个可行流.。
  • 所以在残留网络中不断地求增广路(可行流)不断累加到原图的可行流f中,再更新残留网络,直到新残留网络不存在增广路
  • 如何更新残留网络,对于残留网络的每一个可行流f,残留网络c(u,v)-=f,c(v,u)+=f;即得到一个新的残留网络

最大流最小割定理:

  1. 割的容量:所有正向割边的容量和,不同割的容量不同
  2. 如果f是网络中的一个流,CUT(S,T)是任意一个割,那么f的值等于正向割边的流量与负向割边的流量之差。
  3. 在任何网络中,如果f是一个流,CUT(S,T)是一个割,且f的值等于该割的容量,那么f是一个最大流,CUT(S,T)是一个最小割(容量最小的割)
  4. 最大流最小割定理:在任何网络中,最大流的值等于最小割的容量
  5. 最小割中,其正向割边的流量一定等于割的容量,且如果存在逆向割边的话,逆向流量为0,否则一定还可以增广

一、概念引入

首先要先清楚最大流的含义,就是说从源点到经过的所有路径的最终到达汇点的所有流量和。

      流网络G=(V,E)是一个有向图,其中每条边(u,v)∈E均有一个非负容量c(u,v)>=0。如果(u,v)不属于E,则假定c(u,v)=0。流网络中有两个特别的顶点:源点s和汇点t。下图展示了一个流网络的实例(其中斜线左边的数字表示实际边上的流,右边的数字表示边的最大容量):

image

二、基础知识准备

      对一个流网络G=(V,E),其容量函数为c,源点和汇点分别为s和t。G的流f满足下列三个性质: 
      容量限制:对所有的u,v∈V,要求f(u,v)<=c(u,v)。 
      反对称性:对所有的u,v∈V,要求f(u,v)=-f(v,u)。 
      流守恒性:对所有u∈V-{s,t},要求∑f(u,v)=0 (v∈V)。

容量限制说明了从一个顶点到另一个顶点的网络流不能超过设定的容量,就好像是一个管道只能传输一定容量的水,而不可能超过管道体积的限制;反对称性说明了从顶点u到顶点v的流是其反向流求负所得,就好像是当参考方向固定后,站在不同的方向看,速度一正一负;而流守恒性说明了从非源点或非汇点的顶点出发的点网络流之和为0,这有点类似于基尔霍夫电流定律,且不管什么是基尔霍夫电流定律,通俗的讲就是进入一个顶点的流量等于从该顶点出去的流量,如果这个等式不成立,必定会在该顶点出现聚集或是枯竭的情况,而这种情况是不应该出现流网络中的,所以一般的最大流问题就是在不违背上述原则的基础上求出从源点s到汇点t的最大的流量值,显然这个流量值应该定义为从源点出发的总流量或是最后聚集到t的总流量,即流f的值定义为|f|=∑f(s,v) (v∈V)。

在求解最大流的问题前,必须对三个概念有所了解:残留网络,增广路径和割。下面先给出这三个概念的基本内容。 
      a.在给定的流网络G=(V,E)中,设f为G中的一个流,并考察一对顶点u,v∈V,在不超过容量c(u,v)的条件下,从u到v之间可以压入的额外网络流量,就是(u,v)的残留容量,就好像某一个管道的水还没有超过管道的上限,那么就这条管道而言,就一定还可以注入更多的水。残留容量的定义为:cf(u,v)=c(u,v)-f(u,v)。而由所有属于G的边的残留容量所构成的带权有向图就是G的残留网络。下图就是上面的流网络所对应的残留网络:

对比于:

image


image

残留网络中的边既可以是E里面的边,也可以是此边的反向边。只有当两条边(u,v)和(v,u)中,至少有一条边出现在初始网络中时,边(u,v)才会出现在残留网络中。下面是一个有关残留网络的定理,若f是G中的一个流,Gf是由G导出的残留网络,f'是Gf中的一个流,则f+f'是G中一个流,且其值|f+f'|=|f|+|f'|。证明时只要证明f+f'这个流在G中满足之前所讲述的三个原则即可。在这里只给出理解性的证明,可以想象如果在一个管道中流动的水的总流量为f,而在该管道剩余的流量中存在一个流f'可以满足不会超过管道剩余流量的最大限,那么将f和f'合并后,也必定不会超过管道的总流量,而合并后的总流量值也一定是|f|+|f'|。

b.增广路径p为残留网络Gf中从s到t的一条简单路径。根据残留网络的定义,在不违反容量限制的条件下,G中所对应的增广路径上的每条边(u,v)可以容纳从u到v的某额外正网络流。而能够在这条路径上的网络流的最大值一定是p中边的残留容量的最小值。这还是比较好理解的,因为如果p上的流大于某条边上的残留容量,必定会在这条边上出现流聚集的情况。所以我们将最大量为p的残留网络定义为:cf(p)=min{cf(u,v) | (u,v)在p上}。而结合之前在残留网络中定理,由于p一定是残留网络中从s到t的一条路径,且|f'|=cf(p),所以若已知G中的流f,则有|f|+|cf(p)|>|f|且|f|+|cf(p)|不会超过容量限制。

c.流网络G(V,E)的割(S,T)将V划分成为S和T=V-S两部分,使得s∈S,t∈T。如果f是一个流,则穿过割(S,T)的净流被定义为f(S,T)=∑f(x,y) (x∈S,y∈T),割(S,T)的容量为c(S,T)。一个网络的最小割就是网络中所有割中具有最小容量的割。设f为G中的一个流,且(S,T)是G中的一个割,则通过割(S,T)的净流f(S,T)=|f|。因为f(S,T)=f(S,V)-f(S,S)=f(S,V)=f(s,V)+f(S-s,V)=f(s,V)=|f|(这里的公式根据f(X,Y)=∑f(x,y) (x∈X,y∈Y)的定义,以及前面的三个限制应该还是可以推出来的,这里就不细讲了)。有了上面这个定理,我们可以知道当把流不断增大时,流f的值|f|不断的接近最小割的容量直到相等,如果这时可以再增大流f,则f必定会超过某个最小的割得容量,则就会存在一个f(S,T)<=c(S,T)<|f|,显然根据上面的定理这是不可能。所以最大流必定不超过网络最小割的容量。

综合上面所讲,有一个很重要的定理:最大流最小割定理

如果f是具有源s和汇点t的流网络G=(V,E)中的一个流,则下列条件是等价的: 
      1) f是G中一个最大流。 
      2) 残留网络Gf不包含增广路径。 
      3) 对G的某个割(S,T),有|f|=c(S,T)。


*********************************** 分割线 ***********************************

猜你喜欢

转载自blog.csdn.net/weixin_44630656/article/details/109983889