最大流之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)
### DINIC算法解决最大流问题 #### 原理概述 DINIC算法是一种用于计算网络流中的最大流的有效算法。该算法通过构建层次图并在此基础上查找增广路径来逐步增加流量,直到无法再找到新的增广路径为止[^1]。 #### 层次图的建立 为了提高效率,DINIC算法采用了一种称为“层次图”的结构。具体来说,就是利用宽度优先搜索(BFS)从源节点出发给每一个可达结点分配一个距离标签,表示到达这个顶点至少要经过几条边。只有当存在一条容量大于0且终点未被访问过的有向边时才会继续向下一层扩展[^2]。 #### 寻找增广路径 一旦建立了层次图之后,就可通过深度优先搜索(DFS)尝试沿着这些已标记好的层级关系去探寻可能存在的增广路径。这里需要注意的是,在一次完整的DFS过程中可以一次性找出多条独立互不干扰的最短长度增广路,并据此调整实际流动量[^4]。 #### 当前弧优化技术 普通版本下的DINIC可能会遇到性能瓶颈,尤其是在处理大规模稀疏图的情况下。为此引入了所谓的“当前弧”机制作为改进措施之一。“当前弧”指的是对于每个节点维护其尚未完全探索完毕的第一条有效出边位置指针;每当完成一轮针对某特定起点v的所有潜在目标w之间的遍历时,则更新此指向下一可用候选者处,从而减少不必要的重复扫描操作次数,显著提升整体执行速度[^3]。 ```cpp struct Edge { int to, cap, flow; }; vector<Edge> edges; vector<int> adj[MAX_N]; int level[MAX_N], cur[MAX_N]; void addEdge(int u, int v, int c) { edges.push_back({v, c, 0}); edges.push_back({u, 0, 0}); // 反向边初始化为零流量 adj[u].push_back(edges.size() - 2); adj[v].push_back(edges.size() - 1); } bool bfs(int s, int t) { /* ... */ } int dfs(int node, int sink, int flow) { /* ... */ } // 主函数调用部分省略... while (bfs(sink)) { memset(cur, 0, sizeof(cur)); while (int f = dfs(source, sink, INT_MAX)) max_flow += f; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值