最大流问题介绍
图定义
给定一张有向图,a->b
边的权值表示当前情况下,从a到b能够发送的流量上限是多少,比如 a->b = 10
表示当前能够从a发10流量给b,当然我们也可以选择不发那么多。
例子:
假设a->b
之间未有任何流量的发送,此时我们发8流量,那么 a->b = 10 变为 a->b = 2
,a->b
能够发送的流量上限由10 变为 2
源点与汇点
源点只发不收,汇点只收不发,其他点作为中转站,既可以发也可以收,这像极了快递物流网络。。。
源点:卖家
汇点:买家
其他点:快递物流站点
最大流问题描述
从源点能够想方设法发送到汇点的最大流量的值,而求解这个问题就是求解这个最大值
你可能会想:这不是直接把源点发送的流量拉满就行了🐎?
然而事实是这样的:两点之间发送流量最大值取决于两点之间管道最细的地方的大小(木桶原理),前面塞太多后面放不下。。。
Ford-Fulkerson方法
Ford-Fulkerson不是算法,是一种方法,而通过bfs实现这种方法,叫做Edmonds-Karp算法
回退边与增广图
Ford-Fulkerson定义了一个非常具有创造性的“回退边”
如果我们在a->b
中塞入x流量,表示我们买了x货物,于是我们可以退货。
回退边描述了这种退货的行为,回退边的权值是我们已经塞入的流量的大小(联系现实生活中,买了x货物,最多退货量为x)
我们在原图g的基础上增加回退边,构成原图的增广图ga,增广图包含的回退边的权值等于已经塞入的流量的大小
伪代码
Ford-Fulkerson方法提供了一种求解最大流的思路,是一个很巧妙的贪心思路,这是他的伪代码:
sum = 0
while True:
构造原图g的增广图ga
在增广图ga上找到一条从源点到汇点的最短路径p,这个最短指的是经过的点最少而不是经过的边权值之和最小
if 找不到p :
break
记录p一路走来遇到的"最细"的管道大小f(即经过的所有边中权值的最小值)
通过f来更新原图中路径p上的边:
for 边(a->b) in p:
(a->b)能够通过的流量上限 -= f
(b->a)能够通过的流量上限 += f
sum += f
sum就是图最大流
很抽象
图解
Edmonds-Karp算法
EK算法之所以是算法,在于它确定了在增广图上找最短路径的方法,即BFS,因为bfs的特性,b到终点一定是最短路
复杂度
bfs用邻接表,复杂度为O(e),更新边复杂度O(e),而每次最少增长1的流量,假设图最大流答案是f
,则最多需要f
次bfs
,所以总复杂度为(O(e)+O(e) * f) = O(ef)
代码实现细节
- 因为边具有属性,邻接表
adj[x]
不再存储下一个顶点的信息,而是存储所有从x出发的边,在边集合中的下标 - 一条边具有两个属性:起点,当前允许的最大流量
- 因为回退边的存在,总共边的数量是原图边数量的两倍
- 在bf