最大流问题与Ford-Fulkerson方法
最大流问题是一个经典的网络流问题。想象你有一个输油管道网络,其中有一个源点(如炼油厂)和一个汇点(如港口)。管道有固定的容量(单位时间能通过的最大流量)。问题是如何安排每条管道的流量,使得从源点到汇点的总流量达到最大。Ford-Fulkerson方法是解决此问题的一种基础而重要的框架。
一、问题描述与建模
我们将这个管道网络抽象成一个有向图 G=(V, E)。
V是节点集合(例如管道交汇处、站点)。E是边集合(即管道)。- 每条边
(u, v) ∈ E有一个非负的容量c(u, v) ≥ 0。如果边(u, v)不存在,我们设c(u, v) = 0。 - 我们指定两个特殊节点:源点
s和汇点t。
一个流 f 是一个从 V × V 到实数的函数,它满足以下两个关键约束:
- 容量约束:对于所有
u, v ∈ V,有0 ≤ f(u, v) ≤ c(u, v)。流经一条边的流量不能超过其容量。 - 流量守恒:对于所有
u ∈ V - {s, t},有∑_{v∈V} f(v, u) = ∑_{w∈V} f(u, w)。即,对于除了源点和汇点之外的任何节点,流入该节点的总流量必须等于流出该节点的总流量(既没有存储,也没有凭空产生)。
流的值 |f| 定义为从源点净流出的流量:|f| = ∑_{v∈V} f(s, v) - ∑_{v∈V} f(v, s)。通常假设没有边流入源点,所以 |f| = ∑_{v∈V} f(s, v)。我们的目标就是找到一个流 f,使得其值 |f| 最大。
二、核心思想:残存网络与增广路径
Ford-Fulkerson方法的精髓在于迭代改进。它从一个零流(所有边流量为0)开始,不断寻找可以“推送”更多流量的路径。
-
残存容量与残存网络:
给定图G和当前的流f,对于边(u, v),我们定义其残存容量c_f(u, v)为在此流的基础上,还能再增加多少流量。- 如果原边
(u, v)还有剩余容量,即f(u, v) < c(u, v),那么c_f(u, v) = c(u, v) - f(u, v)。这意味着我们可以沿着这条边的方向再推送最多c_f(u, v)的流量。 - 此外,我们还需要考虑反向的可能性。当前的流
f(v, u)表示有流量从v流向了u。这部分流量实际上是可以撤销(或称“回流”)的。因此,我们为反向边定义一个残存容量c_f(v, u) = f(u, v)。
由所有节点
V和所有残存容量c_f(u, v) > 0的边(包括正向的剩余边和反向的回流边)构成的图G_f,称为残存网络。它精确地描述了在当前流状态下,还有哪些路径可以用于增加流量。 - 如果原边
-
增广路径:
在残存网络G_f中,任何一条从源点s到汇点t的简单路径p,都称为一条增广路径。
这条路径的瓶颈容量c_f(p)定义为该路径上所有边的最小残存容量,即c_f(p) = min{ c_f(u, v) | (u, v) 在路径 p 上 }。
关键操作:我们可以沿着这条增广路径p,再“推送”大小为c_f(p)的流量。这个操作具体如何影响原图的流f呢?- 对于路径
p上的每条正向边(u, v)(即G中的原边),我们把它的流f(u, v)增加c_f(p)。 - 对于路径
p上的每条反向边(v, u)(它对应原边(u, v)),我们相当于撤销了原来从u到v的一部分流,所以把f(u, v)减少c_f(p)。
这个操作始终满足容量约束和流量守恒。直观理解就是:我们找到一条还能通水的路,并把这条路上最窄的一段(瓶颈)所能通过的水量全部推过去。反向边的存在至关重要,它允许算法撤销之前的错误分配(将流从一条边“退回”),从而能找到全局最优解。
- 对于路径
三、Ford-Fulkerson算法步骤
算法流程非常清晰:
- 初始化:对于所有边
(u, v) ∈ E,令f(u, v) = 0。 - 循环:
a. 在当前的流f所对应的残存网络G_f中,使用图搜索算法(如BFS或DFS)寻找一条从s到t的增广路径p。
b. 如果找不到这样的路径,算法终止,当前的f就是最大流。
c. 如果找到了,计算这条路径的瓶颈容量c_f(p)。
d. 沿着路径p更新流f:对于路径上的每条边(u, v):
* 如果(u, v)是E中的正向边,则f(u, v) = f(u, v) + c_f(p)。
* 如果(u, v)是残存网络中的反向边(对应原边(v, u)),则f(v, u) = f(v, u) - c_f(p)。
四、实例演示
考虑一个简单网络,s 为源点,t 为汇点,边及容量如下:
s -> A (10), s -> B (5), A -> B (3), A -> t (7), B -> t (8)
- 迭代1:
- 初始流
f全为0。 - 残存网络
G_f与原图相同(因为所有边残存容量等于原始容量,反向边容量为0,图中未显示)。 - 找到一条增广路径
s -> A -> t,瓶颈容量min(10, 7) = 7。 - 更新流:
f(s, A)=7,f(A, t)=7。
- 初始流
- 迭代2:
- 当前流
f(s,A)=7, f(A,t)=7,其他为0。构建残存网络:- 边
(s,A):剩余10-7=3,正向边s->A容量为3;反向边A->s容量为f(s,A)=7。 - 边
(A,t):剩余7-7=0,正向边消失;反向边t->A容量为7。 - 边
(s,B):剩余5,正向边容量5。 - 边
(A,B):剩余3,正向边容量3。 - 边
(B,t):剩余8,正向边容量8。
- 边
- 找到增广路径
s -> B -> t,瓶颈容量min(5, 8)=5。 - 更新流:
f(s,B)=5,f(B,t)=5。
- 当前流
- 迭代3:
- 当前流:
f(s,A)=7, f(A,t)=7, f(s,B)=5, f(B,t)=5。 - 构建残存网络(略)。可以找到增广路径
s -> A -> B -> t。s->A残存容量为3。A->B残存容量为3。B->t残存容量为8-5=3。
- 瓶颈容量
min(3, 3, 3)=3。 - 更新流:
f(s,A) = 7+3 = 10f(A,B) = 0+3 = 3(注意A->B是正向边)f(B,t) = 5+3 = 8- 同时,
f(A,t)保持不变为7。但注意,B的流量守恒:流入B的f(A,B)=3和f(s,B)=5,总共8;流出B的f(B,t)=8,守恒。
- 当前流:
- 终止:
- 此时流值
|f| = f(s,A) + f(s,B) = 10 + 5 = 15。 - 在最终的残存网络中,从
s出发无法再到达t(例如,s->A已满,s->B已满,且没有其他路径通向t)。算法终止。 - 最终最大流为15。
- 此时流值
五、关键定理与算法分析
- 最大流最小割定理:这是网络流理论的核心定理。它指出,在一个流网络中,从源点
s到汇点t的最大流的值,等于将所有节点划分成包含s的集合S和包含t的集合T后,所有从S指向T的边的容量之和(这个和称为一个(S, T)割的容量)的最小值。这个定理保证了Ford-Fulkerson方法的正确性——当残存网络中不存在s-t路径时,根据当前流自然可以构造出一个割,其容量等于当前流值,从而证明当前流就是最大流。 - 时间复杂度:Ford-Fulkerson方法本身是一个框架,其时间复杂度取决于如何寻找增广路径。
- 如果使用DFS寻找路径,且边容量为整数,每次增广至少增加1个单位的流量。设最大流值为
|f*|,则算法需要O(|f*| * |E|)的时间。在容量很大或不是整数时,可能无法在多项式时间内结束。 - 因此,在实践中通常使用其改进版本——Edmonds-Karp算法,它规定必须使用BFS来寻找增广路径(即每次都找最短的
s-t路径)。这保证了算法在O(|V| * |E|^2)的时间内完成,是确定的多项式时间算法。每次BFS寻找路径是O(|E|),而BFS的次数可以被证明不超过O(|V| * |E|)。
- 如果使用DFS寻找路径,且边容量为整数,每次增广至少增加1个单位的流量。设最大流值为
六、总结
Ford-Fulkerson方法通过不断在残存网络中寻找增广路径并推送流量来逼近最大流。反向边的引入是算法能成功找到全局最优解的关键。当残存网络中不再存在 s 到 t 的路径时,算法终止,此时的流即为最大流。其改进版Edmonds-Karp算法(BFS寻找增广路径)提供了稳定的多项式时间复杂度保证。