1.前言
- 在学习最小费用流算法时,对其算法的设计,条件的设计,算法的流程有很大的疑惑,遂花时间来理解
- 本文仅个人理解,仅供参考
- 阅读本文前,最好已经了解过最大流算法,知道如何寻找增广路
- 博主目前无法证明最小费用流算法的正确定,这里只介绍其原理及流程
- 后续可能会添加实例,或者介绍最大流算法
2.问题
给定流值v
,找到从 s->t
流值为 v
的流中,花费最小的那个流
3.可以写成如下规划
我们在图中,会用一对数表示一条边的信息,(a, b)
:
-
其中 a 代表边的长度,也就是单位花费
-
b 代表边的容量,也就是允许通过的最大流值
4.上述规划问题的对偶问题
将原问题的 x i j ≤ c i j x_{ij} \le c_{ij} xij≤cij 写成 − x i j − 新 变 量 = − c i j -x_{ij}-新变量 = -c_{ij} −xij−新变量=−cij 就很容易写成下面的形式
- 每个变量没有很直观的物理意义,我们构造出来只是为了解决原始问题,但是在后面的分析中,我们能够很直观地看出每个变量的作用
5.互补松紧条件
若原问题的可行解 { x i j } \{x_{ij}\} {xij},对偶问题的可行解 { p i , r i j } \{p_i, r_{ij}\} {pi,rij}分别是原问题和对偶问题的最优解,则其充要条件是互补松紧条件,如下所示:
注意:必须要先是可行解,满足互补松紧条件之后,才能是最优解
我们进一步观察,可以通过上述两个等式,推导出下面两个条件
本算法的妙处之一:构造 r i j = m a x ( 0 , p j − p i − w i j ) r_{ij} = max(0, p_j -p_i-w_{ij}) rij=max(0,pj−pi−wij) ,这使得 r i j ≥ p j − p i − w i j r_{ij} \ge p_j-p_i-w_{ij} rij≥pj−pi−wij 恒成立,且 r i j ≥ 0 r_{ij} \ge 0 rij≥0恒成立,使得 任意的 p i p_i pi 与构造出的 r i j r_{ij} rij 组成的解,都是对偶问题的可行解
-
我们观察对偶问题,可以看到只有两个约束条件
- 对于第一个条件,因为 r i j ≥ p j − p i − w i j r_{ij} \ge p_j-p_i-w_{ij} rij≥pj−pi−wij 恒成立,所以第一个条件满足
- 对于第二个条件,因为 r i j ≥ 0 r_{ij} \ge 0 rij≥0 恒成立,所以满足
-
对 p i p_i pi 是无限制的
-
所以,我们只要取 r i j = m a x ( 0 , p j − p i − w i j ) r_{ij} = max(0, p_j -p_i-w_{ij}) rij=max(0,pj−pi−wij),其与任意的 p i p_i pi 构成的解,都是对偶问题的可行解。这就是我们构造 r i j r_{ij} rij 的目的,保证取到的值是对偶问题的可行解
现在,我们通过使用构造的 r i j r_{ij} rij 来重新看待刚刚得到的两个条件,可以证明
-
第二个式子很好看出来,我们下面给出第一个式子的证明
还记得刚刚的互补松紧条件吗?就是下面这个
我们使用上面通过构造的 r i j r_{ij} rij 得到的两个条件,可以将互补松紧条件等价为
也就是说,只要我们找到了原问题的可行解 { x i j } \{x_{ij}\} {xij},对偶问题的可行解 { p i , r i j } \{p_i, r_{ij}\} {pi,rij}(恒可行),他们满足上面这两个条件,则这两个解分别是原问题和对偶问题的最优解。
这有什么用呢?
- 用处:我们在保证互补松紧条件始终成立的情况下,找到原问题的一个可行解,则这个解就是原问题的最优解(就是找到一条路,如果其流值为 a a a,这条路就是流值为 a a a 的路中,费用最小的)
- 这可以将寻找最小费用流问题转换为:
- 在满足互补松紧条件的情况下,找原问题的可行解
- 原问题中,任意一个 s-t 流都是原问题的可行解。因为我们满足从 s 点流出的流量等于流入 t 点的流量,中间节点流入流量等于流出流量(原问题的约束)
- 假如一条 s-t 流的流量是 v 1 v_1 v1,因为满足互补松紧条件,这个流就是流量是 v 1 v_1 v1 的流中费用最小的,也就是 v 1 v_1 v1 对应的最小费用流
- 如果 v 1 < v v_1 \lt v v1<v,我们就尝试通过寻找增广路,来增加流值,直到找到了流值为 v v v 的解。同样,这个解自然成为流值为 v v v 的时候,费用最小的。
- 通过这种方式,我们可以找到给定流值 v v v 的最小费用流
- 在满足互补松紧条件的情况下,找原问题的可行解
总的来说,互补松紧条件的作用就是:使得我们在找到原问题的可行解的时候(任意一个 s-t 流),如果其满足互补松紧条件,则这个流就是当前流值对应的最小费用流
因为互补松紧条件有这样好的性质,所以下面我们寻找增广路时,要保证不破坏互补松紧条件。
6.最小费用流算法
核心思想:总是在费用最小的增广路上增加流值,直到流值达到 v v v。
我们从 { p i = 0 } \{p_i=0\} {pi=0}(是对偶问题的可行解), { x i j = 0 } \{x_{ij} = 0\} {xij=0}(原始问题不可行)开始
- 通过 r i j = m a x ( 0 , p j − p i − w i j ) r_{ij} = max(0, p_j -p_i-w_{ij}) rij=max(0,pj−pi−wij),计算出 r i j r_{ij} rij
- 当前 { p i , r i j } \{p_i, r_{ij}\} {pi,rij} 是对偶规划的可行解(前面说过,只要 r i j r_{ij} rij 是这样定义出来的,任意 { p i , r i j } \{p_i, r_{ij}\} {pi,rij} 都是对偶问题的可行解)
我们通过增广路,来增大流值,这时的流值变成 v 1 v_1 v1。注意:寻找增广路的时候,不能违背互补松紧条件,否则我们找到一条流,没法说明其是最小费用流。
-
我们看到,互补松紧条件其实定义了边上的流的大小,在 p j − p i ≠ w i j p_j-p_i \ne w_{ij} pj−pi=wij 时,我们是不能通过寻找增广路来增加流值的,因为这些边上的流,要么等于 0,要么等于其容量。如果对这些边进行增广路算法,将破坏互补松紧条件
-
那我们寻找增广路时,只能在 p i − p h = w i j p_i - p_h = w_{ij} pi−ph=wij 上的边中,寻找增广路,来增加流值。
-
如果是前向弧,还需要满足 x i j < c i j x_{ij} \lt c_{ij} xij<cij。(容量没满才能继续增加流量)
-
如果是后向弧,还需要满足 x i j > 0 x_{ij} \gt 0 xij>0。(边上有流量,才有机会退回流量)
-
于是,我们定义可以运行增广路算法的弧,我们称其为允许弧,定义如下
-
我们为了不违反互补松紧条件,只能在允许弧上,搜索增广路。
-
-
我们不断寻找增广路,增加流值,直到找到的流的流值等于 v v v
算法如下:
这个算法很容易理解,就是迭代增加流值
-
每次迭代时,先确定可行弧
-
然后再可行弧上寻找增广路
-
如果能找到 s-t 增广路,则增加流量
-
如果找不到
-
将寻找增广路时,未获得标号的点的 p i p_i pi 值都 + 1
-
这会导致,在标号的点 i i i 和没有标号的点 j j j 之间的 p i − p j p_i - p_j pi−pj 产生差值(因为只有未标号的点的 p i p_i pi 增加了)
-
差值会导致什么呢?
-
差值会导致,我们能够在有标号的点和未标号的点之间的边上,构造出 p i − p j = w i j p_i - p_j = w_{ij} pi−pj=wij 的条件,从而将这条边加入到允许弧的集合中
-
如果 i i i 有两条邻接边,一条费用是3, 一条费用是 10,则费用是 3 的边会先加到允许弧中。因为找不到增路时,每次都是未标号的点 p j + 1 p_j + 1 pj+1,加到第三次时,费用为3的边就会被加入允许弧了。这同时保证了,我们优先走费用小的弧
-
总的来说,这个操作的意义就是:
-
-
-
-
直到流量增加到 v v v,然后返回这个流(就是返回解)
这么看来,最小费用流算法,其实就是在互补松紧条件限制下的最大流算法