网络流入门,EK算法和Dinic算法求最大流


1.什么是网络流

网络流(英语:Network flow)是指在一个每条边都有容量(Capacity)的有向图分配流,使一条边的流量不会超过它的容量。通常在运筹学中,有向图称为网络。顶点称为节点(Node)而边称为弧(Arc)。

以上内容来自维基百科  

1.1网络流的相关定义

  • 源点:只进不出的顶点叫做源点。
  • 汇点:只出不进的顶点叫做汇点。
  • 容量和流量:每条有向边上有两个量,容量和流量。

1.2相关性质

  • 容量限制:对所有顶点对u,v∈V,满足f(u, v) ≦ c(u, v);
  • 反对称性:对所有顶点对u,v∈V,满足f(u, v) = - f(v, u);
  • 流守恒性:对所有顶点对u∈V-{s, t},满足Σv∈Vf(u,v)=0。

这里看不懂也没关系,可以先看一下下面具体思路

1.3求最大流的问题模型

最大流问题就是在容量容许的条件下,从源点到汇点所能通过的最大流量。

  1. 通常可以把这些边想象成道路,流量就是这条道路的车流量,容量就是道路可承受的最大的车流量。很显然的,流量<=容量。而对于每个不是源点和汇点的点来说,可以类比的想象成没有存储功能的货物的中转站,所有“进入”他们的流量和等于所有从他本身”出去”的流量。在每条道路的流量<=容量的情况下,终点的车流量就是这这个图的最大流。
  2. 好比你家是汇 自来水厂是源点,然后自来水厂和你家之间修了很多条水管子接在一起,水管子规格不一 有的容量大 有的容量小,然后问自来水厂开闸放水 你家收到水的最大流量是多少,如果自来水厂停水了 你家那的流量就是0 当然不是最大的流量,但是你给自来水厂交了100w美金 自来水厂拼命水管里通水 但是你家的流量也就那么多不变了 这时就达到了最大流

现在我们也知道了最大流问题相关模型,我们知道了到底要求什么了,但是怎么求?

2.如何求最大流

在这里插入图片描述
我们先看一下这个图,很明显源点是‘0’,汇点是‘5’,下面我们来考虑如何求这个图的最大流。

首先,加入当前所有边上的流量都没有超过容量,那么就把这一组流量,或者说这个流,称为一个可行流。比如零流,即所有的流量都是‘0’的流。

我们就从这个零流开始考虑。假如有一条路,这条路从源点开始一段一段的连到了汇点,并且这条路上的每一段都满足流量<容量,注意,是严格的<,而不是<=。那么我们一定能找到这条路上每一条边的增量(容量-流量)的最小值delta,我们把这条路上的所有边的流量都加上这个delta,一定可以保证这个流是可行的流,这是很显然的。

这样,我们就得到了一个更大的流,他的总流量是之前的流量+delta,而新增加流量的这条路叫做增广路

可以看上图:
刚开始所有边的流量都是零流,我们开始找他的增广路
0-1-3-5是一条可行的增广路,其delta=2,所以这条路增加的流量为delta
0-2-4-5也是一条增广路,这条路上的每条边增加的容量为1

我们不断的寻找增广路,每次都对其进行增广,直到源点和汇点不连通,也就是找不到增广路位置,当找不到增广陆的时候,当前的流量就是最大流!这结论非常重要。

其实以上就是求解最大流的基本思想

3.关于反向边

如果你之前看过其他的博客,应该已经知道了反向边是什么意思,但是这里我还是有必要再次说明的。

在这里插入图片描述
看一下这个网络流模型,当我们寻找增广路时,第一次我们可能找到了 1-2-3-4 这条路,我们对这条路进行增广,增广后我们发现从源点到汇点已经没有路可以走了,所以最大流是‘1’,但是我们明显可以看出来这个图的最大流是‘2’,为什么出现这种情况呢?

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

这时候我们利用反向边的概念,每次增广以后,在把路上每一段的容量减少delta的同时,也把每一段上的反方向的容量增加delta。
这时再找增广路的时候,就会找到1-3-2-4这条可增广量,即delta值为1的可增广路。将这条路增广之后,得到了最大流‘2’。

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

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

4.EK算法和Dinic算法

好了,我在上面已经把最大流的基本思路解释完了,相信你已经对其有所了解,那么我来说一下代码的具体实现,根据代码可以让你更好的了解最大流的算法
无论是EK还是Dinic,他们的算法原理都是寻找增广路直到无路可走,我们先总结一下大概思路:

  • 寻找增广路,这条路从源点开始一直一段一段的连到了汇点,并且,这条路上的每一段都满足流量<容量,注意,是严格的<,而不是<=。
  • 更新残量图,将这条路径上的每一条有向边u->v的残量减去delta,同时对于起反向边v->u的残量加上delta
  • 重复这个步骤,直到无路可走

4.1EK(Edmonds-Karp)算法

增广路径事实上是残留网中从源点s到汇点t的路径,可以利用图算法中的任意一种被算法来获取这条路径,例如BFS,DFS等。其中基于BFS的算法通常称为Edmonds-Karp算法,该算法是“最短”扩充路径,这里的“最短”由路径上的边的数量来度量,而不是流量或者容量。

基本思路:

  1. 找到一条从源点到汇点的路径,使得路径上任意一条边的残量>0(注意是大于而不是大于等于,这意味着这条边还可以分配流量),这条路径便称为增广路
  2. 找到这条路径上最小的F[u][v](我们设F[u][v]表示u->v这条边上的残量即剩余流量),下面记为flow
  3. 将这条路径上的每一条有向边u->v的残量减去flow,同时对于起反向边v->u的残量加上flow
    4.重复上述过程,直到找不出增广路,此时我们就找到了最大流

先看一下这道题 HDU排水沟

这是个模板题,那么我直接贴代码了,你可以通过代码进一步理解最大流EK算法解题思想

#include<iostream>
#include<queue>
#include<string.h>
using namespace std;
const int N = 220;
const int inf=0x3f3f3f3f;
int flag[N],c[N*2][N*2],pre[N];
//flags数组存到这个点的最大流,c存容量,pre存上一个点
queue<int> q;
int m=0,n=0;
int bfs()
{
    memset(pre,-1,sizeof(pre));  //初始化
    pre[1]  = 0;
    flag[1] = inf; //先把起点流量定义为无限大
    q.push(1);
    while(!q.empty())
    {
        int s = q.front();  q.pop();
        for(int i=1;i<=n;i++)
        {
            //如果这个点不是源点并且容量>0并且没有被使用过
            if(i!=1&&c[s][i]&&pre[i]==-1)  
            {
                pre[i] = s;   //存上一个点
                flag[i] = min(c[s][i],flag[s]);//记录当前delta和这个点的容量取最小,也就是求出当前的delta值
                q.push(i);
            }
        }
    }
    if(pre[n]==-1) return -1;    //如果汇点的标记还是-1,说明没有找到增广路,返回-1
    return flag[n];    //返回增广路的delta值
}
int ek()
{
    int sum = 0;
    int a,b;
    while(1)
    {
        int flow = bfs(); //
        if(flow==-1)  break;//如果没有找到增广路就退出
        sum += flow;     //最大流加上增广的delta值
        int  t = n;
        while(t!=1)//更新残量图,从汇点更新到源点
        {
            a = pre[t];
            c[a][t] -= flow;
            c[t][a] += flow;
            t = a;
        }

    }
    return sum;
}
int main()
{
    int l=0;
    while(~scanf("%d %d",&m,&n))
    {
        
        memset(c,0,sizeof(c));
        for(int i=0;i<m;i++)
        {
            int a,b,x;
            scanf("%d %d %d",&a,&b,&x);
            c[a][b] += x;
        }
        printf("%d\n",ek());
        
    }
}

4.2Dinic算法

Dinic算法是基于EK算法的改进,并且两种算法都是由俄罗斯科学家Dinic提出的。Dinic在EK的基础上加入了层次优化,即用BFS分层后用DFS找增广路

算法思路

  1. 首先进行bfs分层网络
  2. 进行dfs多路增广,并且记录残量网络和流量。
  3. 重复上述过程直到不存在从s到t的路径为止。将所有dfs的结果累加起来就是答案。将每一次的增广路效果叠加起来就是图上每条边的流量。

说实话Dinic我还不太会啊啊啊啊啊,主要是存图的方法链式前向星还没看懂,下次我会写篇存图的方法链式前向星

请移步Dinic算法详述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值