(转) 网络流之最大流算法(EdmondsKarp)

求网络流有很多算法,这几天学习了两种,记录一下EK算法。

首先是网络流中的一些定义:

V表示整个图中的所有结点的集合.
E表示整个图中所有边的集合.
G = (V,E) ,表示整个图.
s表示网络的源点,t表示网络的汇点.
对于每条边(u,v),有一个容量c(u,v)   (c(u,v)>=0),如果c(u,v)=0,则表示(u,v)不存在在网络中。相反,如果原网络中不存在边(u,v),则令c(u,v)=0.
对于每条边(u,v),有一个流量f(u,v).

一个简单的例子.网络可以被想象成一些输水的管道.括号内右边的数字表示管道的容量c,左边的数字表示这条管道的当前流量f.

网络流的三个性质:

1、容量限制:  f[u,v]<=c[u,v]
2、反对称性:f[u,v] = - f[v,u]
3、流量平衡:  对于不是源点也不是汇点的任意结点,流入该结点的流量和等于流出该结点的流量和。
只要满足这三个性质,就是一个合法的网络流.

最大流问题,就是求在满足网络流性质的情况下,源点 s 到汇点 t 的最大流量。

求一个网络流的最大流有很多算法 这里首先介绍 增广路算法(EK)

学习算法之前首先看了解这个算法中涉及到的几个图中的定义:

**残量网络

为了更方便算法的实现,一般根据原网络定义一个残量网络。其中r(u,v)为残量网络的容量。
r(u,v) = c(u,v) – f(u,v)
通俗地讲:就是对于某一条边(也称弧),还能再有多少流量经过。
Gf 残量网络,Ef 表示残量网络的边集.

这是上面图的一个残量网络。残量网络(如果网络中一条边的容量为0,则认为这条边不在残量网络中。

r(s,v1)=0,所以就不画出来了。另外举个例子:r(v1,s) = c(v1,s) – f(v1,s) = 0 – (-f(s,v1)) = f(s,v1) = 4.

其中像(v1,s)这样的边称为后向弧,它表示从v1到s还可以增加4单位的流量。

但是从v1到s不是和原网络中的弧的方向相反吗?显然“从v1到s还可以增加4单位流量”这条信息毫无意义。那么,有必要建立这些后向弧吗?

显然,第1个图中的画出来的不是一个最大流。

但是,如果我们把s -> v2 -> v1 -> t这条路径经过的弧的流量都增加2,就得到了该网络的最大流。

注意到这条路径经过了一条后向弧:(v2,v1)。

如果不设立后向弧,算法就不能发现这条路径。

**从本质上说,后向弧为算法纠正自己所犯的错误提供了可能性,它允许算法取消先前的错误的行为(让2单位的流从v1流到v2)

注意,后向弧只是概念上的,在程序中后向弧与前向弧并无区别.

**增广路

增广路定义:在残量网络中的一条从s通往t的路径,其中任意一条弧(u,v),都有r[u,v]>0。


如图绿色的即为一条增广路。

看了这么多概念相信大家对增广路算法已经有大概的思路了吧。

**增广路算法

增广路算法:每次用BFS找一条最短的增广路径,然后沿着这条路径修改流量值(实际修改的是残量网络的边权)。当没有增广路时,算法停止,此时的流就是最大流。

**增广路算法的效率

设n = |V|,  m = |E|

每次增广都是一次BFS,效率为O(m),而在最坏的情况下需要(n-2增广。(即除源点和汇点外其他点都没有连通,所有点都只和s与t连通)

所以,总共的时间复杂度为O(m*n),所以在稀疏图中效率还是比较高的。

hdoj 1532是一道可以作为模板题目练手。

模板代码:

[cpp] view plaincopyprint?

    1. #include <cstdio>
    2. #include <cstring>
    3. #include <iostream>
    4. #include <string>
    5. #include <algorithm>
    6. #include <map>
    7. #include <vector>
    8. using namespace std;
    9. const int N = 1100;
    10. const int INF = 0x3f3f3f3f;
    11. struct Node
    12. {
    13. int to;//终点
    14. int cap; //容量
    15. int rev;  //反向边
    16. };
    17. vector<Node> v[N];
    18. bool used[N];
    19. void add_Node(int from,int to,int cap)  //重边情况不影响
    20. {
    21. v[from].push_back((Node){to,cap,v[to].size()});
    22. v[to].push_back((Node){from,0,v[from].size()-1});
    23. }
    24. int dfs(int s,int t,int f)
    25. {
    26. if(s==t)
    27. return f;
    28. used[s]=true;
    29. for(int i=0;i<v[s].size();i++)
    30. {
    31. Node &tmp = v[s][i];  //注意
    32. if(used[tmp.to]==false && tmp.cap>0)
    33. {
    34. int d=dfs(tmp.to,t,min(f,tmp.cap));
    35. if(d>0)
    36. {
    37. tmp.cap-=d;
    38. v[tmp.to][tmp.rev].cap+=d;
    39. return d;
    40. }
    41. }
    42. }
    43. return 0;
    44. }
    45. int max_flow(int s,int t)
    46. {
    47. int flow=0;
    48. for(;;){
    49. memset(used,false,sizeof(used));
    50. int f=dfs(s,t,INF);
    51. if(f==0)
    52. return flow;
    53. flow+=f;
    54. }
    55. }
    56. int main()
    57. {
    58. int n,m;
    59. while(~scanf("%d%d",&n,&m))
    60. {
    61. memset(v,0,sizeof(v));
    62. for(int i=0;i<n;i++)
    63. {
    64. int x,y,z;
    65. scanf("%d%d%d",&x,&y,&z);
    66. add_Node(x,y,z);
    67. }
    68. printf("%d\n",max_flow(1,m));
    69. }
    70. }
时间: 2024-10-14 13:08:21

(转) 网络流之最大流算法(EdmondsKarp)的相关文章

网络流求最大流算法

一.网络流的定义:有向图G=(V,E)中,点集中有一源点S,一汇点T.且S入度为0,T出度为0.对于每条边edge,都有一权值函数c,表示其容量,一权值函数f,表示其实际流量. 满足对于任意一条边都有f(edge)<=c(edge). 二.最大流的定义:在不违背网络流的定义下,S到T的最大流量. 三.増广路的思想. 我们先考虑一个网络流:红色数字表示实际流量,蓝色表示边的容量,黄色表示更优的流量. 这个流从S到T的流量是5,但其显然不是最优的. 这个流比上面那个优,而且事实上,这个流就是当前网络

网络流之最大流算法

最大流 网络流的定义: 在一个网络(有流量)中有两个特殊的点,一个是网络的源点(s),流量只出不进,一个是网络的汇点(t),流量只进不出. 最大流:就是求s-->t的最大流量 假设 u,v 两个点,连接这两个点的边为e(u,v); 对于每一条边都有一个实际流量f(u,v),还有一个容量c(u,v),就是这条边上可以通过的最大流量. 当一条边的容量c(u,v)=0,证明这条边是不存在的, 作为一个合格的网络流,必须满足三个条件: 1>每条边的实际流量小于等于容量  f(u,v)<=c(u,

关于最大流的EdmondsKarp算法详解

最近大三学生让我去讲课,我就恶补了最大流算法,笔者认为最重要的是让学弟学妹们入门,知道算法怎么来的?为什么是这样?理解的话提出自己的改进,然后再看看Dinic.SAP和ISAP算法….. 一.概念引入 首先要先清楚最大流的含义,就是说从源点到经过的所有路径的最终到达汇点的所有流量和. 流网络G=(V,E)是一个有向图,其中每条边(u,v)∈E均有一个非负容量c(u,v)>=0.如果(u,v)不属于E,则假定c(u,v)=0.流网络中有两个特别的顶点:源点s和汇点t.下图展示了一个流网络的实例(其

【网络流】最大流最简单的Ford-Fulkerson算法

Ford-Fulkerson算法是一个非常好理解的算法.大概是这样子的: ①不断从起点开始dfs 找一个通向终点的路.如果一条都找不到了,那么当前的值就是最大流 ②如果还存在着通向终点的路,那么加上它的最短的一截,然后做出图的残余图.继续. 下面是基于邻接矩阵的Ford-Fulkerson最大流算法.简单易懂,老少皆宜. #include <iostream> #include <cstdio> #include <cstring> #include <cmath

网络最大流算法

网络最大流是指在一个网络流图中可以从源点流到汇点的最大的流量.求解网络最大流的常用算法可以分为增广路径算法和预推进算法.其中,预推进算法的理论复杂度优于增广路径算法,但是编码复杂度过高,且效率优势在很多时候并不是很明显,因此,经常使用的算法为增广路径算法.     增广路径算法主要有Fold-Fulkerson算法,Edmonds-Karp算法,Dinic算法,ISAP算法.其中,Fold-Fulkerson 是最基本的增广路径思想,不能算作严格的算法实现. 增广路径     增广路径算法的思想

最大流算法(Edmons-Karp + Dinic 比较) + Ford-Fulkson 简要证明

Ford-Fulkson用EK实现:483ms #include <cstdio> #include <cstring> #define min(x,y) (x>y?y:x) int pre[105],q[105]; int F[105][105]; int n,nc,np,m,s,t,all; int MaxFlow(int s, int t){ int ans=0; while(1){ memset(pre,0,sizeof(pre)); int head=0,tail=

HDU 3468 Treasure Hunting(BFS+网络流之最大流)

题目地址:HDU 3468 这道题的关键在于能想到用网络流.然后还要想到用bfs来标记最短路中的点. 首先标记方法是,对每一个集合点跑一次bfs,记录所有点到该点的最短距离.然后对于任意一对起始点来说,只要这个点到起点的最短距离+该点到终点的最短距离==起点到终点的最短距离,就说明这点在某条从起点到终点的最短路上. 然后以集合点建X集,宝物点建Y集构造二分图,将从某集合点出发的最短路中经过宝物点与该集合点连边.剩下的用二分匹配算法或最大流算法都可以.(为什么我的最大流比二分匹配跑的还要快....

有上下界的、有多组源汇的、网络流、费用流问题

先默认读者有基础的网络流以及费用流的知识前置 1.有上下界无源点汇点的可行流问题: 在本文中指: 原图中没有任何一个点可以凭空产生流量,亦没有任何一个点可以凭空消灭流量: 存在边既有流量上界又有流量下界: 求每条边流量的一组可行解: 满足每个点的入流量等于出流量: 由题意可见本题的图中有环,于是此类问题也被称作循环流: 这里给出的解法是将本题转换为一道普通的有上界最大流问题: 修改本题原图中每条边的流量下界为0,上界为原上界-原下界: 视为该边现在已经拥有了等同于该边流量下界的基础流量了, 然而

最大流算法和理论的简单理解

我退坑很久了,这篇文章不是因为刷OJ而写的总结,毕竟菜鸡老年人, 23333 之前我学网络流看的是算法书和别人的博客然后就开始套板子,而这次因为考试不得已把课本上的定理都看了一遍,瞬间对最大流算法更加清楚了. 1.定义 在学习网络流算法前要了解的一些知识. 1.对于一个有向的网络$G=(N, A, C)$,其中的$c_{i,j}$表示弧$(i,j)\in A$的容量,并设$s,t$为发点和收点,令$$x_{i,j}=通过弧(i,j)的流量$$2.对于一个满足流量限制的流量称之为可行流,其中限制条