使用循环抵消算法求解最小费用流问题
- https://opt.aliyun.com/studio/market/model/detail/C3C0CFAB-FEA3-4D34-A3E9-F694072A91E4/
这里写目录标题
- 最小费用流问题(Minimum Cost Flow Problem, MCFP)
- 问题定义&数学模型
- 常用算法
- 应用场景
- 示例
- 连续最短路算法 (Successive Shortest Path Algorithm)
- 问题实例
- 求解步骤 (使用连续最短路算法)
- CCA算法(Cycle Canceling Algorithm,循环抵消算法)
最小费用流问题(Minimum Cost Flow Problem, MCFP)
道路系统、水管网络或数据网络是催生"流问题"这类优化问题的实际背景。这类系统的共同特征在于:某种资源需要通过图的边进行传输,而每条边都有流量承载上限的约束。在某些情况下,边还具有其他属性,因此需要为特定边的使用分配成本——例如高速公路网中可变的通行费。在这种背景下,如何以最低成本将特定流量通过网络输送就成为一个值得研究的问题。该问题被称为"最小费用流问题"。
最小费用流问题(Minimum Cost Flow Problem, MCFP) 是图论和运筹学中的经典问题,旨在以最低的总成本将一定数量的“流”(如货物、数据、电力等)从网络中的起点运输到终点,同时满足每条路径的容量限制。
问题定义&数学模型
给定一个有向图 G = ( V , E ) G = (V, E) G=(V,E),其中:
- V V V:节点集合(如工厂、仓库等)。
- E E E:边的集合(如运输路径),每条边 ( u , v ) ∈ E (u, v) \in E (u,v)∈E 有两个属性:
- 容量 c ( u , v ) c(u, v) c(u,v):边上的最大流量限制。
- 费用 a ( u , v ) a(u, v) a(u,v):单位流量通过该边的成本。
- 流量需求 d d d:
- 每个节点 v ∈ V v \in V v∈V 有一个净流量需求 b ( v ) b(v) b(v):
- 若 b ( v ) > 0 b(v) > 0 b(v)>0,表示 v v v 是源点(供应节点,需流出流量)。
- 若 b ( v ) < 0 b(v) < 0 b(v)<0,表示 v v v 是汇点(需求节点,需流入流量)。
- 若 b ( v ) = 0 b(v) = 0 b(v)=0,表示 v v v 是中转节点(流量守恒)。
- 所有节点的净流量需满足 ∑ v ∈ V b ( v ) = 0 \sum_{v \in V} b(v) = 0 ∑v∈Vb(v)=0。
- 每个节点 v ∈ V v \in V v∈V 有一个净流量需求 b ( v ) b(v) b(v):
目标:在所有满足流量约束的可行流中,找到总费用最小的流。
设 f ( u , v ) f(u, v) f(u,v) 为边 ( u , v ) (u, v) (u,v) 上的流量,数学模型可表示为:
min ∑ ( u , v ) ∈ E a ( u , v ) ⋅ f ( u , v ) \min \sum_{(u,v) \in E} a(u,v) \cdot f(u,v) min(u,v)∈E∑a(u,v)⋅f(u,v)
约束条件:
- 流量守恒(对所有节点 v ∈ V v \in V v∈V):
∑ u ∣ ( u , v ) ∈ E f ( u , v ) − ∑ w ∣ ( v , w ) ∈ E f ( v , w ) = b ( v ) \sum_{u \mid (u,v) \in E} f(u,v) - \sum_{w \mid (v,w) \in E} f(v,w) = b(v) u∣(u,v)∈E∑f(u,v)−w∣(v,w)∈E∑f(v,w)=b(v) - 容量限制(对所有边 ( u , v ) ∈ E (u,v) \in E (u,v)∈E):
0 ≤ f ( u , v ) ≤ c ( u , v ) 0 \leq f(u,v) \leq c(u,v) 0≤f(u,v)≤c(u,v)
模型关键特点:
- 费用与流量线性相关:总费用是流量的线性函数。
- 多源多汇:允许存在多个供应点和需求点。
- 可行解存在条件:所有源点的总供应量必须等于所有汇点的总需求量。
常用算法
- 单纯形法的网络流版本:
- 针对网络流问题的特殊结构设计的线性规划解法。
- 消圈算法(Cycle Canceling):
- 通过消除负费用圈逐步优化流量。
- 成功最短路径算法(Successive Shortest Path):
- 类似最大流算法,每次沿最小费用增广路径增加流量。
- 原始-对偶算法(Primal-Dual):
- 结合对偶理论调整流量和势能。
应用场景
- 物流运输:最小化从仓库到客户的运输成本。
- 电力分配:优化电网中电力的传输费用。
- 通信网络:数据包路由的成本最小化。
- 资源调度:如人力资源或生产材料的分配。
示例
假设有一个运输网络:
- 源点 S S S( b ( S ) = 5 b(S) = 5 b(S)=5),汇点 T T T( b ( T ) = − 5 b(T) = -5 b(T)=−5)。
- 边 ( S , A ) (S, A) (S,A):容量 3,费用 2;边 ( S , B ) (S, B) (S,B):容量 4,费用 5。
- 边 ( A , T ) (A, T) (A,T):容量 3,费用 1;边 ( B , T ) (B, T) (B,T):容量 4,费用 3。
最优解:
- 从 S S S 经 A A A 到 T T T 流 3 单位(费用 2 + 1 = 3 2+1=3 2+1=3 每单位,总费用 9)。
- 从 S S S 经 B B B 到 T T T 流 2 单位(费用 5 + 3 = 8 5+3=8 5+3=8 每单位,总费用 16)。
- 总最小费用: 9 + 16 = 25 9 + 16 = 25 9+16=25。
连续最短路算法 (Successive Shortest Path Algorithm)
解决最小费用流问题最经典和直观的算法是 连续最短路算法 (Successive Shortest Path Algorithm)。其基本思想可以概括为:
- 初始化:流量为0,总费用为0。
- 寻找增广路:在残留网络 (Residual Graph) 中,寻找一条从源点 (S) 到汇点 (T) 的费用最小的路径(也叫“最短路”)。
- 增广:沿着这条最短路尽可能多地推送流量(流量大小受路径上各边剩余容量的限制)。
- 更新:更新总流量和总费用,并根据推送的流量更新残留网络。
- 重复:重复步骤2-4,直到推送的总流量达到预设的目标,或者在残留网络中再也找不到从S到T的路径。
问题实例
假设有一个物流网络,需要从仓库(源点S)向一个客户(汇点T)运送 5个单位 的货物。网络中的中转站、路径、每条路径的容量(最多能运多少货)和单位运费如下图所示。
目标: 在满足运送5个单位货物的前提下,如何安排运输路线才能使得总运输费用最低?
网络结构:
- 节点:S (源点), A (中转站), B (中转站), T (汇点)
- 边 (路径):每条边有
(容量, 单位费用)
两个属性。- S -> A:
(容量=4, 费用=2)
- S -> B:
(容量=5, 费用=4)
- A -> B:
(容量=3, 费用=1)
- A -> T:
(容量=2, 费用=3)
- B -> T:
(容量=6, 费用=2)
- S -> A:
求解步骤 (使用连续最短路算法)
初始状态:
- 总流量 = 0
- 总费用 = 0
- 需要发送的总流量 = 5
迭代 1: 发送第一批货物
-
寻找费用最短路:
在当前的图中,我们寻找从 S 到 T 的费用最小的路径。- 路径1: S -> A -> T,费用 = 2 + 3 = 5
- 路径2: S -> B -> T,费用 = 4 + 2 = 6
- 路径3: S -> A -> B -> T,费用 = 2 + 1 + 2 = 5
我们发现有两条路径费用同为5,可以任选其一。我们选择 S -> A -> B -> T。
-
确定增广流量:
这条路径上的边的容量分别是:cap(S,A)=4
,cap(A,B)=3
,cap(B,T)=6
。
路径的瓶颈容量是min(4, 3, 6) = 3
。
所以,我们这次最多可以发送 3个单位 的货物。 -
更新状态:
- 本次增加费用: 流量 × 单位费用 = 3 × 5 = 15
- 总流量: 0 + 3 = 3
- 总费用: 0 + 15 = 15
- 剩余待发送流量: 5 - 3 = 2
-
更新残留网络:
- 对于路径 S -> A -> B -> T 上的每条边,其正向边的容量减少3。
- S -> A: 剩余容量
4 - 3 = 1
- A -> B: 剩余容量
3 - 3 = 0
(这条路饱和了) - B -> T: 剩余容量
6 - 3 = 3
- S -> A: 剩余容量
- 同时,创建或增加反向边,其容量为3,费用为正向边的相反数。这是为了“反悔”机制,如果后续发现更优的路径,可以通过反向边将已发送的流量“退回”。
- A -> S: 容量
+3
, 费用-2
- B -> A: 容量
+3
, 费用-1
- T -> B: 容量
+3
, 费用-2
- A -> S: 容量
- 对于路径 S -> A -> B -> T 上的每条边,其正向边的容量减少3。
迭代 2: 发送第二批货物
我们还需要发送 2 个单位的货物。
-
寻找费用最短路:
在更新后的残留网络中寻找 S 到 T 的最短路。- 路径1: S -> A -> T,费用 = 2 + 3 = 5。该路径可用,因为 S->A 还有1的容量。
- 路径2: S -> B -> T,费用 = 4 + 2 = 6。
- 路径 S -> A -> B -> T 不再可用,因为 A->B 的容量为0。
最短路是 S -> A -> T。
-
确定增广流量:
这条路径上的边的剩余容量是:cap(S,A)=1
,cap(A,T)=2
。
瓶颈容量是min(1, 2) = 1
。
所以,我们这次可以发送 1个单位 的货物。 -
更新状态:
- 本次增加费用: 1 × 5 = 5
- 总流量: 3 + 1 = 4
- 总费用: 15 + 5 = 20
- 剩余待发送流量: 5 - 4 = 1
-
更新残留网络:
- S -> A: 剩余容量
1 - 1 = 0
(这条路也饱和了) - A -> T: 剩余容量
2 - 1 = 1
- 增加反向边:
- A -> S: 容量
+1
, 费用-2
(现在 A->S 总容量为 3+1=4) - T -> A: 容量
+1
, 费用-3
- A -> S: 容量
- S -> A: 剩余容量
迭代 3: 发送最后一批货物
我们还需要发送 1 个单位的货物。
-
寻找费用最短路:
在最新的残留网络中寻找 S 到 T 的最短路。- S->A 这条边已经饱和,所以 S->A->… 的路径都不能从S直接出发了。
- 只剩下路径 S -> B -> T。费用 = 4 + 2 = 6。
- (注意:此时可能存在包含反向边的更短路径,例如 S->B->A->T,费用为 4 + (-1) + 3 = 6。但在这个简单例子里,直接路径更优)。
最短路是 S -> B -> T。
-
确定增广流量:
这条路径上的边的剩余容量是:cap(S,B)=5
,cap(B,T)=3
。
瓶颈容量是min(5, 3) = 3
。
但我们只需要再发送 1个单位 的货物。所以实际增广流量为1。 -
更新状态:
- 本次增加费用: 1 × 6 = 6
- 总流量: 4 + 1 = 5
- 总费用: 20 + 6 = 26
- 剩余待发送流量: 5 - 5 = 0
最终结果
我们已经成功发送了5个单位的货物。
- 最小总费用: 26
- 流量分配方案:
- 沿着路径 S -> A -> B -> T 发送 3 个单位。
- 沿着路径 S -> A -> T 发送 1 个单位。
- 沿着路径 S -> B -> T 发送 1 个单位。
验证最终流量:
- S -> A: 3 + 1 = 4 (容量4,已满)
- S -> B: 1 (容量5,未满)
- A -> B: 3 (容量3,已满)
- A -> T: 1 (容量2,未满)
- B -> T: 3 + 1 = 4 (容量6,未满)
所有流量均未超过边的容量限制,总发出流量为5,总接收流量也为5,符合要求。
CCA算法(Cycle Canceling Algorithm,循环抵消算法)
CCA算法(Cycle Canceling Algorithm,循环抵消算法,也称为消圈算法) 是一种用于求解最小费用流问题(Minimum Cost Flow Problem)的经典优化算法。它通过迭代识别并消除残余网络中的负成本环(negative-cost cycles)来逐步降低总运输成本,最终得到费用最优的流量分配方案。
CCA算法 的核心思想:
- 找到一个初始可行流(如零流或通过其他方法构造)。
- 在残余网络中寻找负费用圈(negative cost cycle)(即沿该圈增流可降低总成本)。
- 沿负费用圈增流,更新残余网络,直到无负费用圈为止。
注:残余网络中的边 ( u , v ) (u,v) (u,v) 费用为 a ( u , v ) a(u,v) a(u,v),反向边 ( v , u ) (v,u) (v,u) 费用为 − a ( u , v ) -a(u,v) −a(u,v)。
- https://algorithms.discrete.ma.tum.de/graph-algorithms/flow-cycle-cancelling/index_en.html
- https://ocw.mit.edu/courses/15-082j-network-optimization-fall-2010/a91028424cba24db109e5d6f4ea7a835_MIT15_082JF10_av14.pdf