Flok

xiaoxiao2021-02-28  724

我也不是很理解,但是学习之后过了这道题,先贴上来看一看吧。 在介绍着三种概念之前,我们先简单介绍下 Ford-Fulkerson 方法的基本思想。首先需要了解的是 Ford-Fulkerson 是一种 迭代 的方法。开始时,对所有的 u,v 属于 V f(u,v)=0( 这里 f(u,v) 代表 u v 的边当前流量 ) ,即初始状态时流的值为 0 。在每次迭代中,可以通过寻找一个“ 增广路径 ”来增加流值。增广路径可以看做是从源点 s 到汇点 t 之间的一条路径,沿该路径可以压入更多的流,从而增加流的值。反复进行这一过程,直到增广路径都被找出为止。 举个例子来说明下,如图所示,每条红线就代表了一条增广路径,当前 s t 的流量为 3 当然这并不是该网络的最大流,根据寻找增广路径的算法我们其实还可以继续寻找增广路径,最终的最大流网络如下图所示,最大流为4

接下来我们就介绍如何寻找增广路径。在介绍增广路径之前,我们首先需要介绍残留网络的概念。

一、残留网络

顾名思义,残留网络是指给定网络和一个流,其对应还可以容纳的流组成的网络。具体说来,就是假定一个网络G=VE),其源点s,汇点t。设fG中的一个流,对应顶点u到顶点v的流。在不超过Cuv)的条件下(C代表边容量),从uv之间可以压入的额外网络流量,就是边(uv)的残余容量residual capacity),定义如下:

ruv=cuv-fuv

举个例子,假设(uv)当前流量为3/4,那么就是说cuv=4fuv=3,那么ruv=1

我们知道,在网络流中还有这么一条规律。uv已经有了3个单位流量,那么从反方向上看,也就是从vu就有了3个单位的残留网络,这时rvu=3。可以这样理解,uv3个单位流量,那么从vu就有了将这3个单位流量的压回去的能力

我们来具体看一个例子,如下图所示一个流网络

其对应的残留网络为:

二、增广路径

在了解了残留网络后,我们来介绍增广路径。已知一个流网络G和流f增广路径p是其残留网络Gf中从st的一条简单路径。形象的理解为从st存在一条不违反边容量的路径,向这条路径压入流量,可以增加整个网络的流值。上面的残留网络中,存在这样一条增广路径:

其可以压入4个单位的流量,压入后,我们得到一个新的流网络,其流量比原来的流网络要多4这时我们继续在新的流网络上用同样的方法寻找增广路径,直到找不到为止。这时我们就得到了一个最大的网络流。

三、流网络的割

上面仅仅是介绍了方法,可是怎么证明当无法再寻找到增广路径时,就证明当前网络是最大流网络呢?这就需要用到最大流最小割定理

首先介绍下,割的概念。流网络GVE)的割(ST)将V划分为ST=V-S两部分,使得s属于St属于T割(ST)的容量是指从集合S到集合T的所有边(有方向)的容量之和(不算反方向的,必须是S-àT。如果f是一个流,则穿过割(ST)的净流量被定义为fST)(包括反向的,SàT的为正值,T>S的负值)。将上面举的例子继续拿来,随便画一个割,如下图所示:

割的容量就是c(u,w)+c(v,x)=26

当前流网络的穿过割的净流量为f(u,w)+f(v,x)-f(w,v)=12+11-4=19

显然,我们有对任意一个割,穿过该割的净流量上界就是该割的容量,即不可能超过割的容量。所以网络的最大流必然无法超过网络的最小割。

可是,这跟残留网络上的增广路径有什么关系呢?

首先,我们必须了解一个特性,根据上一篇文章中讲到的最大流问题的线性规划表示时,提到,流网络的流量守恒的原则,根据这个原则我们可以知道,对网络的任意割,其净流量的都是相等的。具体证明是不难的,可以通过下图形象的理解下,

和上面的割相比,集合S中少了uv,从源点s到集合T的净流量都流向了uv,而在上一个割图中,集合S到集合T的流量是等于uv到集合T的净流量的。其中w也有流流向了uv,而这部分流无法流向源点s,因为没有路径,所以最后这部分流量加上suv的流量,在uv之间无论如何互相传递流,最终都要流向集合T,所以这个流量值是等于s流向uv的值的。将s比喻成一个水龙头,uv流向别处的水流,都是来自s的,其自身不可能创造水流。所以任意割的净流量都是相等的。

万事俱备,现在来证明当残留网络Gf中不包含增广路径时,fG的最大流。

假设Gf中不包含增广路径,即Gf不包含从sv的路径,定义S={v:Gf中从sv存在一条通路},也就是Gfs能够有通路到达的点的集合,显然这个集合不包括t,因为st没有通路。这时,我们令T=V-S。那么(ST)就是一个割。如下图所示:

那么,对于顶点u属于Sv属于T,有fuv=cuv)。否则(uv)就存在残余流量,因而su加上uv就构成了一条sv的通路,所以v就必须属于S,矛盾。因此这时就表明当前流f是等于当前的割的容量的,因此f就是最大流。

1,第一个数表示容量cij,第二个数表示流量fij 

2,可行流与最大流 

在运输网络的实际问题中,我们可以看出,对于流有两个显然的要求:一是每个弧上的流量不能超过该弧的最大通过能力(即弧的容量);二是中间点的流量为0,源点的净流出量和汇点的净流入量必相等且为这个方案的总输送量。因此有: 

(1)容量约束:0fijcij(vi,vj)E 

(2)守恒条件 

 

3,可增广路径 

所谓可增广路径,是指这条路径上的流可以修改,通过修改,使得整个网络的流值增大。 

设f是一个可行流,P是从源点s到汇点t的一条路,若p满足下列条件: 

    (1)p上的所有前向弧(vivj)都是非饱和弧,即0fij<cij 

    (2)p上的所有后向弧(vivj)都是非零弧,即0<fijcij 

则称p为(关于可行流f的)一条可增广路径。 

 

4,最大流定理 

当且仅当不存在关于f*的增广路径,可行流f*为最大流。 

 

5,最大流算法 

算法思想:最大流问题实际上是求一可行流{fij},使得v(f达到最大。若给了一个可行流f,只要判断N中有无关于f的增广路径,如果有增广路径,改进f, 得到一个流量增大的新的可行流;如果没有增广路径,则得到最大流。 

 

6,应用:http://acm.pku.edu.cn/JudgeOnline/problem?id=1273 

代码:(有环的情况存在) 

Cpp代码  

#include <iostream>  #include <queue>    using namespace std;    const int maxN=201;    static int edge[maxN][maxN];  bool visited[maxN];  int father[maxN];  int N, M; //边数,顶点数  int ans; //结果  void Ford_Fulkerson( )  {        while(1)      {          //一次大循环,找到一条可能的增广路径          queue <int> q;          memset(visited, 0, sizeof(visited));          memset(father, -1, sizeof(father));          int now;          visited[0] = true;          q.push(0);          while(!q.empty())//广度优先          {              now = q.front();              q.pop();              if(now == M-1) break;              for(int i = 0; i < M; i++)              {                  //每次父亲节点都要更新,权值减为0的边就不算了.                  if(edge[now][i] && !visited[i])                  {                      father[i] = now;                      visited[i] = true;                      q.push(i);                  }              }          }          //可能的增广路不存在了          if(!visited[M-1]) break;          int u, min = 0xFFFF;          for(u = M-1; u; u = father[u])//找出权值最小的边          {              if(edge[father[u]][u] < min)                  min = edge[father[u]][u];          }          //减去最小权值          for(u = M-1; u; u = father[u])          {              //前向弧减去              edge[father[u]][u] -= min;              //后向弧加上              //存在圆环,这句话关键              edge[u][father[u]] += min;          }          //当前增广路径增加的流          ans += min;      }  }    int main()  {      int s, e, w;      while(cin >> N >> M)      {          ans = 0;          memset(edge, 0, sizeof(edge));          for(int i = 0; i<N; i++)          {              cin >> s >> e >> w;              edge[s-1][e-1] += w;          }          Ford_Fulkerson();          cout << ans << endl;      }      return 0;  }  
转载请注明原文地址: https://www.6miu.com/read-43348.html

最新回复(0)