网络流学习笔记
网络流学习笔记
注:笔者比较菜,许多证明没有给出,一些地方可能有误。
一些定义
网络
对于一张有向图 $G \in (V, E)$,有一个源点 $S \in V$ 和一个汇点 $T \in V$,每一条边 $(u, v) \in E$ 有一个限流 $c(u, v)$。特别的,$\forall (u, v) \notin E \ , \ c(u, v) = 0$。
网络流
对于一个函数 $f(u, v)$,若满足如下性质:
- $f(u, v) \le c(u, v)$,即流量小于限流;
- $f(u, v) = -f(v, u)$,反边的流量等于这条边流量的相反数,为什么要有反边我们在下面解释;
- $\forall x \in V - { S, \ T}, \ \sum {(u, x) \in E} f(u,x) = \sum{(x, u) \in E}f(x, u)$,从源点流出的流量等于流入汇点的流量;
则函数 $f(u, v)$ 被称为流函数。
最大流
求从源点出发,能够流到汇点的最大流量问题,称为最大流。
在了解求解算法之前,我们需要了解一些概念。
残量网络
$c(u, v)$ 在减去 $f(u, v)$ 后得到的剩余流量 $c’(u, v)$ 组成的网络被称为残量网络。这个网络可以比原图少一些边($c(x, y) \neq 0, \ c’(x, y) = 0$),也可以有一些原图没有的边(反边)。
增广路
在残量网络的一条从 $S$ 到 $T$ 所有边剩余流量大于零的路径称为增广路。因为所有边剩余流量大于零,所以可以在增广路上在增加一些流量使得流更大,所以最大流的残量网络一定没有增广路。
EK (Edmonds-Karp) 算法
$\tt{EK}$ 算法就是基于增广路的。算法的思想是每次 $\tt{bfs}$ 找到一条增广路,并更新边权造出残量网络,更新答案,直到图中没有增广路。这里解释一下为什么要有反边,因为当前找到的增广路不一定是最优的,可能不包含在最大流的路径中,直接做就会出现错误。而反边给了我们反悔的机会,走反边就相当于减少了原来走这条边的流量,从而保证算法的正确性。
具体的,我们在残量网络上从源点 $S$ 开始 $\tt{bfs}$ ,每次走一直到汇点 $T$ 停止,记录路径上的的边的流量的最小值 $v$ 这样,就找到了一条增广路。接着重新走一遍增广路,每一条边的流量减去 $v$ ,反边流量加上 $v$ ,最后答案也加上 $v$,这样就找到了最大流。
TIP
如果你使用邻接表存图,那么有一个小技巧找到反边:我们都知道,$2$ 异或 $1$ 是 $3$,$3$ 异或 $1$ 是 $2$,因此可以用异或一的方法快速找到反边。
$\tt{EK}$ 的时间复杂度是 $O(nm^2)$,实际较快,能跑 $10^3$ ~ $10^4$ 的数据。
Dinic 算法
$\tt{EK}$ 一次只找到了一条增广路,存在较大的浪费,完全可以一次找到多条,就有了 $\tt{Dinic}$。
$\tt{Dinic}$ 的算法流程是这样的:
- 通过 $\tt{bfs}$ 从源点 $S$ 对原图进行分层,构造分层图,直到汇点 $T$;
- 从源点 $S$ 开始 $\tt{dfs}$,每次只走到层数比当前层数多一的点,找到当前分层图中的所有增广路并更新。
为什么要分层?通过分层可以找到最短路,最短路就可以避免一些问题,像这个图:
最短路就不会搞中间这条边了。以上纯属我口胡,我也不知道对不对。
实现中还有一些必要的优化,可以看下面的代码。经过优化的 $\tt{Dinic}$ 时间复杂度是 $\Theta(n^2m)$,实际跑时则更快,可以过 $10^4$ ~ $10^5$ 的数据。
CODE
$\tt{EK}$ 比较菜我就不放了 (〃´-ω・) 其实是懒得写,主要还是用 $\tt{Dinic}$。
1 |
|
这也是 模板题 AC 代码。
最大流求二分图最大匹配
源点 $S$ 连向左边的点,右边的点连向汇点 $T$,二分图无向边改为左边连向右边的边,所有边边权都为一,这样就把二分图最大匹配转换成了最大流。求方案可以遍历中间的边,若剩余流量为 $0$ 说明这条边被用了。
特别的,用 $\tt{Dinic}$ 求二分图最大匹配复杂度为 $\Theta(\sqrt nm)$ 。
CODE
给出建图部分以及求方案部分的代码。
1 |
|
最小割
删去一些边,使得源点和汇点不连通,求删去的边流量之和最小的问题称为最小割。
结论:最小割 = 最大流,因此最小割代码和最大流一模一样。
参考:《算法竞赛进阶指南》,OI Wiki。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!