EK算法的时间复杂度为O(VE^2),效率比较低。使用最短路径最大流的SAP算法,时间复杂度为O(EV^2),速度快很多。
EK算法使用BFS计算增广路径,耗费时间长。SAP算法在求增广路径时,引入了距离标号,加快了计算求增广路径的过程。
下面介绍一些概念:
距离标号:每个结点到宿结点所需要经过的弧数量的下界。
允许弧:d[u] = d[v] + 1,并且c[u, v]>0的边称为允许弧。每次都走允许弧可以保证走的路径时最短路径。
那么SAP的算法框架可以归纳如下:
1. 用BFS初始化网络中结点的距离标号。
2. 从源点依次往后遍历允许弧到宿点。得到一个增广路。
3. 更新残量网络。重复步骤1~3。
4. 当更新到某个点找不到允许弧时,更新当前结点的d。更新方法d[u] = min(d[vi] + 1)。然后回溯到上一个点重新允许弧。当没有任何可达边时,复制为无效值。
5. 当源点的d值大与网络中结点的数量时,表示网络中再无增广路,结束循环。
编码实现如下:
#-*- coding: utf-8 -*-
topo = { 1 : {2 : 2, 3 : 5},
2 : {1 : 0, 3 : 2, 4 : 2},
3 : {1 : 0, 2 : 0, 4 : 3},
4 : {2 : 0, 3 : 0} }
def init_dis(s, t, dis):
if s == t: return
for node in topo[t]:
if topo[node][t] > 0 and dis[node] == 0:
dis[node] = dis[t] + 1
init_dis(s, node, dis)
def get_augment_path(s, t, dis, pre, remain):
if s == t: return remain
for node, cap in topo[s].items():
if cap > 0 and dis[s] == dis[node] + 1 and pre[node] == 0:
if remain > cap: remain = cap
pre[node] = s
rst = get_augment_path(node, t, dis, pre, remain)
if rst != 999:
topo[s][node] = topo[s][node] - rst
topo[node][s] = topo[node][s] + rst
return rst
else: # 回溯
rst = get_augment_path(node, t, dis, pre, remain)
if rst != 999:
topo[s][node] = topo[s][node] - rst
topo[node][s] = topo[node][s] + rst
return rst
else: # 没有允许弧,更新d
min_d = 999
for node in topo[s]:
if dis[node] + 1 < min_d and topo[s][node] > 0: min_d = dis[node] + 1
dis[s] = min_d
return 999
def sap(s, t):
dis = [0 for i in range(len(topo) + 1)]
init_dis(1, 4, dis)
flow = 0
while dis[s] < len(topo):
pre = [0 for i in range(len(topo) + 1)]
rst = get_augment_path(1, 4, dis, pre, 999)
if rst != 999: flow = flow + rst
print flow
if __name__ == '__main__':
sap(1, 4)