Dinic算法是一种用于网络流中最大流的增广路算法,其时间复杂度为$O(n^2 \times m)$,但大多数情况下会远远优于此时间复杂度。
基本概念
从Menci神犇的博客复制而来。我觉得这写的是很好的一篇介绍,除了代码风格不太喜欢。
容量: ${capacity}(e)$ 表示一条有向边 $e(u,v)$ 的最大允许的流量。
流量: ${flow}(e)$ 表示一条有向边 $e(u,v)$ 总容量中已被占用的流量。
剩余容量(残量):即 $capacity(e)−flow(e)$,表示当前时刻某条有向边 $e(u,v)$ 总流量中未被占用的部分。
反向边:原图中每一条有向边在残量网络中都有对应的反向边,反向边的容量为$0$,容量的变化与原边相反;『反向边』的概念是相对的,即一条边的反向边的反向边是它本身。
残量网络:在原图的基础之上,添加每条边对应的反向边,并储存每条边的当前流量。残量网络会在算法进行的过程中被修改。
增广路(augmenting path):残量网络中从源点到汇点的一条路径,增广路上所有边中最小的剩余容量为增广流量。
增广(augmenting):在残量网络中寻找一条增广路,并将增广路上所有边的流量加上增广流量的过程。
层次: $level(u)$ 表示节点 $u$ 在层次图中与源点的距离。
层次图:在原残量网络中按照每个节点的层次来分层,只保留相邻两层的节点的图,满载(即流量等于容量)的边不存在于层次图中。
思路
用文字叙述大概如下:
1. 建立以出发点为源点的层次图(即源点到各店的距离)
2. 在层次图&残量网络中寻找增广路,并增广流量
3. 重复2直到找不到增广路
4. 重复123直到不存在层次图
实现
建立层次图使用bfs,而寻找增广路则是使用dfs递归增广。
具体实现的时候也有一定的技巧,在代码里面有注释。
反向边存在的意义是什么呢?形象来说其实就是给你一个后悔的机会,往一边流去之后还能再回来。注意反向边的容量在我这里初始为0。
有一个优化就是当前弧优化。这个优化是很显而易见的。如果这条边在当前层次图下找不到路,那么这条边在当前层次图内就再也不会用到。所以我们单开一个cur数组,记录目前遍历到的边,这样就可以进行优化。
代码
以Luogu P3376为例
|
|