网络流的SAP算法

来源:互联网 发布:wemall源码下载 编辑:程序博客网 时间:2024/05/18 02:24

 

网络流的最短增广路算法
在网络流问题中,最常见的方法是Ford-Fulkerson方法。这种方法每次找出一条残留网络中的增广路径并进行增广。整个算法运行过程满足流的三个性质(容量限制、反对称性、流守恒性)。与此相对的预流推进算法不满足流守恒性。在Ford-Fulkerson方法中,如何寻找增广路径是影响算法效率的主要因素。一种好的思想是每次寻找长度最短的路径。按照这种思想,如果采用BFS寻找增广路径,时间复杂度为O(n*m^2),效率不能令人满意。本文介绍一种最短增广路算法(Shortest Augmenting Path Algorithm),它的复杂度为O(n^2*m)。在实践中,这种算法效率还是比较高的。另外,著名的DINIC算法也与此算法有着密切联系。

在SAP算法中,我们定义每个顶点的距离标号(Distance Labels),即残留网络中这个点到汇点的距离。然后,我们只在距离标号相邻的点间寻找增广路径。如果从一个点出发没有容许边,就对重标记并回溯(类似预流推进)。首先介绍一些定义:
残留容量:容量-流量。即,残留容量r[i,j]=c[i,j]-f[i,j]。如果只求流值,则在网络流算法中只需记录残量,而不必记录容量和流量。
残留边、残留网络:有残留容量的边成为残留边,由网络的点集、残留边集构成残留网络。
距离标号 :对于每个点i,定义其在当前残留网络中到汇点的距离为d[i]。如d[t]=0。
容许边:当且仅当r[i,j]>0且d[i]=d[j]+1时,边(i,j)是容许边。我们的算法只考虑容许边。

算法的一开始,我们从汇点进行一次BFS求出距离标号。然后从源点开始递归操作。对于一个点i的操作,如果存在容许边(i,j),当j是汇点时增广并从源点开始递归,否则就令i是j的前驱,并递归j。否则对i重标号,使得d[i]=min{d[j]}+1 ( r[i,j] )>0,并回溯。当d[s]>=n时算法停止,此时不存在任何一条增广路。为提高效率可以改递归为迭代。可以参考一下《Network Flows: Theory, Algorithms, and Applications》中的伪代码:

view sourceprint?
01.algorithm shortest augmenting path;
02.  begin
03.    x:=0;
04.    obtain the exact distance labels d(i);
05.    i:=s;
06.    while d(s)<n do
07.      begin
08.        if i has an admissible arc then
09.          begin
10.            advance(i);
11.            if i=t then augment and set i=s
12.          end
13.        else retreat(i)
14.      end;
15.  end;
16.  
17.procedure advance(i);
18.  begin
19.    let (i,j) be an admissible arc in A(i);
20.    pred(j):=i and i:=j;
21.  end;
22.  
23.procedure retreat(i);
24.  begin
25.    d(i):=min{d(j)+1: (i,j)∈A(i) and r[i,j]>0};
26.    if i<>s then i:=pred(i);
27.  end;
28.  
29.procedure augment;
30.  begin
31.    using the predecessor indices identify an augmenting path P from the source to the sink;
32.    δ:=min{r[i,j] : (i,j)∈P}
33.    augment δ units of flow along path P;
34.  end;

同样类似预流推进,我们有一个重要优化:间隙优化。即我们定义numb(k)是距离标号为k的点的个数。在重标记点i时,我们检查numb(d[i])。若numb(d[i])减为0,算法马上结束。我们很容易发现,点i被重标记则其距离标号必然小于n。而此时不存在此标号的顶点,则容许网络中必然不存在一条增广路径,因此算法结束。这个优化使得算法效率显著提高。

容易证明,SAP的复杂度为O(n^2*m)。而如果采用动态树,复杂度降为O(nmlogn)。只是动态树太过复杂。

提一下DINIC算法:
DINIC算法与SAP十分相似。只是,SAP只是开始进行一次BFS,每次只对单个顶点重标号。而DINIC不对单个顶点重标号,而是多次BFS计算标号。我们套用SAP算法并稍微修改:在过程retreat(i)中,不改变d[i],而是标记i为阻塞的。而容许边(i,j)的定义,其中j必须未阻塞。当源点阻塞,此时形成阻塞流,我们就进行一次反向BFS求得距离标号。由于每次BFS源点的距离标号单调递增,当它大于等于n时算法结束。

p.s.您可以无视本文,直接翻开《Network Flows: Theory, Algorithms, and Applications》(Ahuja, R.K.)

原创粉丝点击