最大流

来源:互联网 发布:剑网3炮姐捏脸数据 编辑:程序博客网 时间:2024/04/27 15:32

最大流


总体上来说,最大流算法分为两大类:增广路 (Augmenting Path) 和预流推进重标号 (Push Relabel) 。也有算法同时借鉴了两者的长处,如 Improved SAP 。本篇主要介绍增广路类算法,思想、复杂度及实际运行效率比较,并试图从中选择一种兼顾代码复杂度和运行效率的较好方案。以下我们将会看到,有时理论分析的时间复杂度并不能很好的反映一种算法的实际效率。

1.Ford - Fulkerson方法

所有增广路算法的基础都是 Ford - Fulkerson 方法。称之为方法而不是算法是因为 Ford - Fulkerson 只提供了一类思想,在此之上的具体操作可有不同的实现方案。

给定一个有向网络 G(V,E) 以及源点 s 终点 t ,FF 方法描述如下:

Ford-Fulkerson方法 (G,s,t)
1 将各边上流量 f 初始化为 0
2 while 存在一条增广路径 p
3     do 沿路径 p 增广流量 f
4 return f

假设有向网络 G 中边 (i,j) 的容量为 c(i,j) ,当前流量为 f(i,j) ,则此边的剩余流量即为 r(i,j) = c(i,j) -f(i,j) ,其反向边的剩余流量为 r(j,i) = f(i,j) 。有向网中所有剩余流量 r(i,j) > 0 的边构成残量网络 Gf,增广路径p即是残量网络中从源点 s 到终点 t 的路径。

沿路径 p 增广流量 f 的操作基本都是相同的,各算法的区别就在于寻找增广路径 p 的方法不同。例如可以寻找从 s 到 t 的最短路径,或者流量最大的路径。


2.Edmonds - Karp算法

Shortest Augmenting Path(SAP) 是每次寻找最短增广路的一类算法,Edmonds - Karp 算法以及后来著名的 Dinic 算法都属于此。SAP 类算法可统一描述如下:

ShortestAugmenting Path
1 x<-- 0
2 while 在残量网络 Gx 中存在增广路 s~> t
3     do 找一条最短的增广路径 P
4        delta <-- min{rij:(i,j) 属于 P}
5        沿 P 增广 delta 大小的流量
6        更新残量网络 Gx
7 return x

在无权边的有向图中寻找最短路,最简单的方法就是广度优先搜索 (BFS),E-K 算法就直接来源于此。每次用一遍 BFS 寻找从源点 s 到终点 t 的最短路作为增广路径,然后增广流量 f 并修改残量网络,直到不存在新的增广路径。

E-K 算法的时间复杂度为 O(VE2),由于 BFS 要搜索全部小于最短距离的分支路径之后才能找到终点,因此可以想象频繁的 BFS 效率是比较低的。实践中此算法使用的机会较少。


3.Dinic 算法

BFS 寻找终点太慢,而 DFS 又不能保证找到最短路径。1970年 Dinic 提出一种思想,结合了 BFS 与 DFS 的优势,采用构造分层网络的方法可以较快找到最短增广路,此算法又称为阻塞流算法 (Blocking FlowAlgorithm)。

首先定义分层网络 AN(f)。在残量网络中从源点 s 起始进行 BFS,这样每个顶点在 BFS 树中会得到一个距源点 s 的距离 d,如 d(s) = 0,直接从 s 出发可到达的点距离为 1,下一层距离为2 ... 。称所有具有相同距离的顶点位于同一层,在分层网络中,只保留满足条件 d(i) + 1 = d(j) 的边,这样在分层网络中的任意路径就成为到达此顶点的最短路径。

Dinic 算法每次用一遍 BFS 构建分层网络 AN(f),然后在 AN(f) 中一遍 DFS 找到所有到终点 t 的路径增广;之后重新构造 AN(f),若终点 t 不在 AN(f) 中则算法结束。DFS 部分算法可描述如下:

 1 p<-- s
 2 while s 的出度 > 0 do
 3     u <-- p.top
 4     if u != t then
 5         if u 的出度 > 0 then
 6             设 (u,v) 为 AN(f) 中一条边
 7             p <-- p, v
 8         else
 9             从 p 和 AN(f) 中删除点 u 以及和 u 连接的所有边
10     else
11         沿 p 增广
12         令 p.top 为从 s 沿 p 可到达的最后顶点
13 end while

 实际代码中不必真的用一个图来存储分层网络,只需保存每个顶点的距离标号并在 DFS 时判断 dist[i] + 1 = dist[j] 即可。Dinic 的时间复杂度为 O(V2E)。由于较少的代码量和不错的运行效率,Dinic 在实践中比较常用。具体代码可参考 DD_engi 网络流算法评测包中的标程,这几天 dinic 算法的实现一共看过和比较过将近 10 个版本了,DD 写的那个在效率上是数一数二的,逻辑上也比较清晰。

 

4.Improved SAP 算法

 本次介绍的重头戏。通常的 SAP 类算法在寻找增广路时总要先进行 BFS,BFS 的最坏情况下复杂度为 O(E),这样使得普通 SAP 类算法最坏情况下时间复杂度达到了 O(VE2)。为了避免这种情况,Ahuja 和 Orlin 在1987年提出了Improved SAP 算法,它充分利用了距离标号的作用,每次发现顶点无出弧时不是像 Dinic 算法那样到最后进行 BFS,而是就地对顶点距离重标号,这样相当于在遍历的同时顺便构建了新的分层网络,每轮寻找之间不必再插入全图的 BFS 操作,极大提高了运行效率。国内一般把这个算法称为 SAP...显然这是不准确的,毕竟从字面意思上来看 E-K 和 Dinic 都属于 SAP,我还是习惯称为 ISAP 或改进的 SAP 算法。

 与 Dinic 算法不同,ISAP 中的距离标号是每个顶点到达终点 t 的距离。同样也不需显式构造分层网络,只要保存每个顶点的距离标号即可。程序开始时用一个反向 BFS 初始化所有顶点的距离标号,之后从源点开始,进行如下三种操作:(1)当前顶点 i 为终点时增广 (2) 当前顶点有满足 dist[i] = dist[j] + 1 的出弧时前进 (3) 当前顶点无满足条件的出弧时重标号并回退一步。整个循环当源点 s 的距离标号 dist[s] >= n 时结束。对 i 点的重标号操作可概括为 dist[i] = 1 +min{dist[j] : (i,j)属于残量网络Gf}。具体算法描述如下:

algorithmImproved-Shortest-Augmenting-Path
 1 f <-- 0
 2 从终点 t 开始进行一遍反向 BFS 求得所有顶点的起始距离标号 d(i)
 3 i <-- s
 4 while d(s) < ndo
 5     if i = t then    //找到增广路
 6         Augment
 7         i <-- s      //从源点 s 开始下次寻找
 8     if Gf 包含从 i 出发的一条允许弧 (i,j)
 9         then Advance(i)
10         else Retreat(i)   // 没有从 i出发的允许弧则回退
11return f

procedure Advance(i)
1(i,j) 为从 i 出发的一条允许弧
2 pi(j) <-- i   // 保存一条反向路径,为回退时准备
3 i<-- j        //前进一步,使 j 成为当前结点

procedure Retreat(i)
1 d(i) <--1 + min{d(j):(i,j)属于残量网络Gf}   // 称为重标号的操作
2 if i != s
3     then i <-- pi(i)   // 回退一步

procedure Augment
1 pi 中记录为当前找到的增广路 P
2delta <-- min{rij:(i,j)属于P}
3 沿路径 P 增广 delta 的流量
4 更新残量网络 Gf

 算法中的允许弧是指在残量网络中满足 dist[i] = dist[j] + 1 的弧。Retreat 过程中若从 i 出发没有弧属于残量网络 Gf 则把顶点距离重标号为 n 。

 虽然 ISAP 算法时间复杂度与 Dinic 相同都是 O(V2E),但在实际表现中要好得多。要提的一点是关于 ISAP 的一个所谓 GAP 优化。由于从 s 到 t 的一条最短路径的顶点距离标号单调递减,且相邻顶点标号差严格等于1,因此可以预见如果在当前网络中距离标号为 k (0 <= k < n) 的顶点数为 0,那么可以知道一定不存在一条从 s 到 t 的增广路径,此时可直接跳出主循环。在我的实测中,这个优化是绝对不能少的,一方面可以提高速度,另外可增强 ISAP 算法时间上的稳定性,不然某些情况下 ISAP 会出奇的费时,而且大大慢于 Dinic 算法。相对的,初始的一遍 BFS 却是可有可无,因为 ISAP 可在循环中自动建立起分层网络。实测加不加 BFS 运行时间差只有 5% 左右,代码量可节省 15~20 行。



0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 五个月宝宝吃手睡觉怎么办 2个月宝宝有蛲虫怎么办 四个月婴儿漏屎怎么办 母乳喂养6个月才11斤怎么办 过期的果泥肉泥怎么办? 孩子被开水烫了怎么办 小儿喝开水烫了怎么办 小孩不识字怎么办17-18 3岁宝宝不识数字怎么办 小孩数字写反了怎么办 3岁宝宝乱啃东西怎么办 闹钟的指针松了怎么办 手机想让它横屏怎么办 教孩子写作业头都被气炸了怎么办 2岁宝宝大小脸怎么办 宝宝2岁半不认识颜色怎么办 宝宝11个多月突然排斥妈妈怎么办 5个月婴儿粘妈妈怎么办 3岁宝宝记不住颜色怎么办 3岁宝宝不认字怎么办 墙纸被宝宝弄上各种颜色怎么办 三周半的孩子不认识数字怎么办 3岁幼儿不会数数怎么办 四岁宝宝不认识数字怎么办 三岁宝宝不认识数字怎么办 4岁半了不认识数字怎么办 5岁小朋友数字不认识怎么办 孩子读一年级字都不识几个怎么办 宝宝二周多了不分颜色怎么办 4岁儿童手指脱皮怎么办 孩子老是不会写2怎么办 宝宝胃浅容易吐怎么办 农村户口小孩去城里读书怎么办 小孩从城市回农村读书怎么办 天冷了怎么办教案反思 小孩上幼儿园不爱学习怎么办 曰光灯管监控要反光怎么办 立邦乳胶漆墙面脏了怎么办 橱柜门黑色边颜色花了怎么办 地板上有真实漆怎么办 吸了泡泡球的气怎么办