最大流之Dinic算法

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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值