前文回顾:https://www.cnblogs.com/ofnoname/p/18731222
想象你是一家快递公司的调度员,每天的任务是将货物从仓库高效送到客户。你设计了一条完美路线:每辆卡车都走最短路径,运费最省,按时送达------直到有一天,某个司机突然上报了一个诡异的现象:
"老板,我的卡车在某个路口绕圈转了10次,运费反而比直送更便宜!"
你眉头一皱,打开监控系统,发现某个环形路口形成了一个**"流量漩涡"**:卡车反复绕圈,既不从仓库出发,也不抵达客户,但总运费却在下降。这合理吗?
前置知识闪电回顾
在之前的冒险中,我们已经征服了两个关键算法:
- 最大流:用 Dinic 或 Edmonds-Karp 打通网络,让流量像冲向终点。
- 无负环的最小费用流 :每次沿着残留网络中的最短费用路径增广,保证每一步都最省钱。
那时的世界是简单的------最短路径增广法,坚定地朝着汇点前进,从不绕路,因为图中没有负费用边,在图里绕圈既不能增加最大流,也不能降低费用,却浪费的宝贵的边容量限制。
但当负环出现时,情况就不一样了
现实中的交通网络可能有这样的阴谋:
- 某条环形公路的过路费为负数(比如政府补贴绕路减排)。
- 卡车司机发现:与其直奔终点,不如在环上多绕几圈"白嫖"补贴,总运费反而更低!
在算法世界中,这就是负费用环的破坏力:
- 满足节点流量平衡和边容量限制的情况下,某一部分流量可以既不来自源点,也不流向汇点,而是在环上无限循环。
- 传统的最短路径增广法会陷入死循环(bellman-ford / spfa 不能解决负环的最短路)。
"如何在不减少总流量的前提下,通过科学'绕圈'把运费降到最低?"
这要求我们跳出原来的思维定式,负环可以是敌人,也可以是盟友。既然如此,我们可以在残留网络中精准追踪这些费用黑洞,将它们变成降低费用的优化工具。
"当流量开始打转,费用流的江湖便多了一条潜规则:真正的极简主义,不是拒绝绕路,而是消灭所有不必要的绕路。"
有负费用环的费用流问题
我们已经知道,初始没有负费用环的图,可以运行最短费用路径的增广,保证永不出现负环,但其复杂度并不稳定;如果我们使用了其他增广(如直接使用 dinic ),可以得到最大流,但其中就会出现负环,并非最小费用;而初始有负费用环的图,更需要我们直面负环。
绕圈如何"薅羊毛"?
假设存在一个负费用环 $ C $,其总费用为 $ \sum_{(u,v) \in C} c(u,v) = -10 $,环上最小残余容量为 $ \delta = 3 $。
- 增广操作:沿环推送 $ \delta = 3 $ 的流量。
- 费用变化:总费用减少 $ 3 \times (-10) = -30 $。
- 流量守恒:环上的流量增减相互抵消,总送达量不变。但费用通过负环被降低。
\[\text{新费用} = \text{旧费用} + \delta \cdot \sum_{(u,v) \in C} c(u,v) \]
因为 $ \sum c(u,v) < 0 $,所以费用不升反降。我们通过在负环上绕圈降低了费用,且不影响最终流量。

消圈算法:把"黑洞"变成"提款机
我们这样来实现消圈:
- Step 1. 暴力输出最大流:用任意最大流算法(如 Dinic)等算法快速铺开流量,首先达成最大流,但此时费用可能不是最优。
- Step 2. 精细化消圈:在残留网络中"排雷"。在残留网络中用 Bellman-Ford / SPFA 扫描负费用环,
- Step 3. 增广负环:选择一个负环 $ C $,通过负环增广优化费用。沿 $ C $ 推送尽可能多的流量,直到环上某条边容量耗尽。减去相应的费用。这个负环也就被消除了。
重复检测与打击,直到网络恢复无负环。
为什么这么做?
定理1 :沿负环增广不改变总流量。
证明:
- 设原流为 $ f $,沿环 $ C $ 增广 $ \delta $,得到新流 $ f' $。
- 对任意节点 $ u \in C $,流入增量 = 流出增量(环上每边增减 $ \delta $),故流量守恒。
- 源点 $ s $ 和汇点 $ t $ 不参与环 $ C $,因此 $ |f'| = |f| $。
直觉上看,有一部分流量既不从源点出发,也不到达汇点。而是一直在图中占据边容量转圈以减小费用。某一个节点的流出/流入量可能比源点更多!
定理2 :流差分解定理
设 $ f $ 和 $ f' $ 为两个不同的最大流。定义差流 $ \Delta = f' - f $,则 $ \Delta $ 满足:
- 流量守恒:对任意节点 $ u $, $ \sum_{(v,u) \in E} \Delta(v,u) = \sum_{(u,v) \in E} \Delta(u,v) $;
- 容量约束:$ |\Delta(u,v)| \leq \text{残余容量}(u,v) $。
差流 $ \Delta $ 可分解为若干简单环的叠加,即:
\[\Delta = \sum_{C \in \mathcal{C}} \delta_C \cdot \chi_C \]
其中 $ \chi_C $ 为环 $ C $ 的指示函数,$ \delta_C $ 为沿环 $ C $ 的增广量。
这个定理符合直觉较容易理解,因为对两个最大流做差:
\[\text{Cost}(f') - \text{Cost}(f) = \sum_{(u,v) \in E} c(u,v) \cdot \Delta(u,v) = \sum_{C \in \mathcal{C}} \delta_C \cdot \sum_{(u,v) \in C} c(u,v). \]
若 $ \text{Cost}(f') < \text{Cost}(f) $,则至少存在一个环 $ C $ 满足:
\[\sum_{(u,v) \in C} c(u,v) < 0 \quad \text{(负费用环)}. \]
实际上,"不存在负费用环"和"是费用最小的最大流"是充分必要条件。所有最大流的差异均可表示为若干环的叠加。若存在费用更小的最大流,其差流中必含负费用环;反之,若残留网络无负费用环,则当前流费用最小。
所以,消圈算法只需要先找到任意一个最大流,通过消除其中的负环,逐步优化费用,最终总是可以得到最小费用最大流。
负环的高效选择策略
假设残留网络中存在多个负环,选择哪一个呢?这直接关系到算法的正确性。你的名字叫做 Bellman-Ford,是一名负环猎人,当你冲进残留网络。眼前出现了三个负环:
- 环A:总费用-30,长度10条边(平均每条-3)。
- 环B:总费用-8,长度2条边(平均每条-4)。
- 环C:总费用-100,长度50条边(平均每条-2)。
问题:你会优先消灭哪个环?
- 无脑选择:随机选择一个环,这使得复杂度和值域相关,达到指数级复杂度
- 菜鸟选择:总费用最低的环C(-100),以为能省最多钱。
- 高手选择:平均费用最低的环B(-4/边),这才是真正的"性价比之王"。
最小平均费用环(MMCC)消圈算法
定义 :
对环 $ C $,其平均费用为:
\[\mu(C) = \frac{\sum_{(u,v) \in C} c(u,v)}{|C|} \]
即费用之和除以总长度。算法每一轮会选择找到 $ \mu(C) $ 最小的负环。
操作步骤:
- 选择消圈:和朴素的消圈一样,在残留网络中循环"排雷"并消圈,直到不存在负圈。
- 计算最小平均费用环:怎样找到平均费用最小的环?动态规划是方法之一。
- 以 \(f_{k,j}\) 表示源点刚好走 \(k\) 条边到达节点 \(j\) 的费用最短路径,那么可用 \(f_{k,j} = \min_{(i,j) \in A} \left\{ f_{k-1,i} + c_{ij} \right\}\) 完整递推。
- 接下来枚举环的起点和长度获得最小的平均费用:\(\mu^* = \min_{j \in \mathbb{N}, 0 \leq k \leq n-1} \left( \frac{f_{n,j} - f_{k,j}}{n - k} \right)\)。
- 将原图费用边权视为都加上 \(|\mu^*|\),再运行最短路径算法,此时的零权重环就是原图的最小平均费用环。一轮的复杂度一共是 \(O(mn)\)。
- 数学魔法:每次消灭该环后,可以保证残留网络的最小平均费用 $ \mu_{\text{min}} $ 严格不降。
单次操作的复杂度上界是 \(O(mn)\);每次消灭操作减少的费用与 $ \mu_{\text{min}} $ 相关。可以此用数学知识证明:总迭代次数被证明为 $ O(m^2 n \log n) $。
策略 | 时间复杂度 |
---|---|
随便选一个负环 | $ O(m^2nCU) $ 和边权相关 |
最小平均费用环(MMCC) | $ O(m^3 n^2 \log n) $ |
代码略。