Flok_Fulkerson算法

我也不是很理解,但是学习之后过了这道题,先贴上来看一看吧。 在介绍着三种概念之前,我们先简单介绍下 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代码  

    1. #include <iostream>  
    2. #include <queue>  
    3.   
    4. using namespace std;  
    5.   
    6. const int maxN=201;  
    7.   
    8. static int edge[maxN][maxN];  
    9. bool visited[maxN];  
    10. int father[maxN];  
    11. int N, M; //边数,顶点数  
    12. int ans; //结果  
    13. void Ford_Fulkerson( )  
    14. {  
    15.   
    16.     while(1)  
    17.     {  
    18.         //一次大循环,找到一条可能的增广路径  
    19.         queue <int> q;  
    20.         memset(visited, 0, sizeof(visited));  
    21.         memset(father, -1, sizeof(father));  
    22.         int now;  
    23.         visited[0] = true;  
    24.         q.push(0);  
    25.         while(!q.empty())//广度优先  
    26.         {  
    27.             now = q.front();  
    28.             q.pop();  
    29.             if(now == M-1) break;  
    30.             for(int i = 0; i < M; i++)  
    31.             {  
    32.                 //每次父亲节点都要更新,权值减为0的边就不算了.  
    33.                 if(edge[now][i] && !visited[i])  
    34.                 {  
    35.                     father[i] = now;  
    36.                     visited[i] = true;  
    37.                     q.push(i);  
    38.                 }  
    39.             }  
    40.         }  
    41.         //可能的增广路不存在了  
    42.         if(!visited[M-1]) break;  
    43.         int u, min = 0xFFFF;  
    44.         for(u = M-1; u; u = father[u])//找出权值最小的边  
    45.         {  
    46.             if(edge[father[u]][u] < min)  
    47.                 min = edge[father[u]][u];  
    48.         }  
    49.         //减去最小权值  
    50.         for(u = M-1; u; u = father[u])  
    51.         {  
    52.             //前向弧减去  
    53.             edge[father[u]][u] -= min;  
    54.             //后向弧加上  
    55.             //存在圆环,这句话关键  
    56.             edge[u][father[u]] += min;  
    57.         }  
    58.         //当前增广路径增加的流  
    59.         ans += min;  
    60.     }  
    61. }  
    62.   
    63. int main()  
    64. {  
    65.     int s, e, w;  
    66.     while(cin >> N >> M)  
    67.     {  
    68.         ans = 0;  
    69.         memset(edge, 0, sizeof(edge));  
    70.         for(int i = 0; i<N; i++)  
    71.         {  
    72.             cin >> s >> e >> w;  
    73.             edge[s-1][e-1] += w;  
    74.         }  
    75.         Ford_Fulkerson();  
    76.         cout << ans << endl;  
    77.     }  
    78.     return 0;  
    79. }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值