Dinic是一个分层的增广路算法。
Dinic中分层的概念定义为:到原结点的最小距离相同的点为同一层。
假设有拓扑如下:如果源点为1,宿点为4;拓扑可以分成4层。
首先,回想E-K算法是怎么算的:使用DFS在1到4之间计算一条增广路,并计算残量网络;再进行循环。我们观察计算第一次增广路(1->2->3->4)之后的残量网络,橙色的表示反向弧。
从图中可以看出,其实这个时候1->2->5->4明显还可以流过一个流量。Dinic将这些信息利用起来。如果某个层次还有可以流到下个层次的流量,就继续计算。那么在遍历到第1层的时候,除了要考虑<2,3>的容量,还要判断<2,5>的容量。
从代码级别看,在DFS时,计算了增广路径1->2->3->4之后,在回溯到2结点的时候,再继续考虑从2结点出发的下一个从1层流向2层的边(<2,5>)。具体细节可见后面代码实现。
那么,Dinic只需要一次DFS就可以实现多次增广。
Dinic算法的步骤如下:
1. 利用BFS建立分层图。
2. 在分层图中,利用DFS在拓扑中求出增广路径(相当于多条),并求出残量网络。
3. 重复步骤1,2;直到宿结点不在分层网络中,表示此时已经没有从源到宿的增广路,结束增广。
分析一下Dinic的时间复杂度:
Dinic算法进行了若干此循环,每次循环中的操作包括一次BFS和一次DFS。而这样的循环最多为V次。因此Dinic的时间复杂度为O(E*V^2)。
代码实现:
#-*- coding: utf-8 -*-
# topo = {from : {to : cap}}
topo = { 1 : {2 : 5},
2 : {1 : 0, 3 : 5, 5 : 5},
3 : {2 : 0, 4 : 4},
4 : {3 : 0, 5 : 2},
5 : {2 : 0, 4 : 4}}
def bfs_get_layer_net(S, D):
layer_net = [0xFFFFFFFF for i in range(len(topo) + 1)]
layer_net[S] = 0
for src, dsts in topo.items():
for dst, flow in dsts.items():
if flow > 0 and layer_net[src] + 1 < layer_net[dst]:
layer_net[dst] = layer_net[src] + 1
return layer_net if layer_net[D] != 0xFFFFFFFF else None
def dfs_argument(layer_net, cur, S, D, min_flow):
layer_flow = 0
if D == cur : return min_flow
for dst, flow in topo[cur].items():
if layer_net[cur] + 1 == layer_net[dst] and flow > 0:
ans = dfs_argument(layer_net, dst, S, D, min(min_flow - layer_flow, flow))
topo[cur][dst] -= ans
topo[dst][cur] += ans
layer_flow += ans # 重要,之后的层次进行增广的时候,最大的流量为前面的层次流下来的网络,因此在这里要进行累加。
return layer_flow
def dinic(S, D):
ans = 0
while True:
layer_net = bfs_get_layer_net(S, D)
if layer_net is None: break
ans += dfs_argument(layer_net, 1, 1, 4, 0xFFFFFF)
return ans
if __name__ == '__main__':
print dinic(1, 4)