最小费用最大流问题的网络单纯形算法
字数 2907 2025-12-15 22:10:56

最小费用最大流问题的网络单纯形算法


题目描述

给定一个有向图,每条边有容量(最多能通过多少流量)和单位费用(每单位流量通过该边所需的成本)。
有一个源点(source)和一个汇点(sink),问:
在不超过每条边容量的前提下,从源点发送尽可能多的流量到汇点,并且使得总费用最小
这就是最小费用最大流(Minimum Cost Maximum Flow, MCMF)问题。
网络单纯形算法是求解该问题的一种高效方法,它结合了单纯形法(线性规划)与图的结构特性


解题思路

网络单纯形算法可以看作单纯形法在最小费用流问题上的特化版本,它直接在图上操作,利用生成树结构来维护一个可行流,并不断进行换基操作(pivot)来降低费用,直到达到最优。


1. 问题形式化

设图 \(G = (V, E)\),每条边 \(e = (u, v)\) 有:

  • 容量 \(cap(e) \geq 0\)
  • 单位费用 \(cost(e)\)(可正可负)
  • 流量 \(flow(e)\)(待求变量)

我们要最小化总费用:

\[\min \sum_{e \in E} cost(e) \cdot flow(e) \]

约束条件:

  1. 容量约束:\(0 \leq flow(e) \leq cap(e)\)
  2. 流量守恒(除源点 \(s\) 和汇点 \(t\)):

\[ \sum_{e \in \delta^+(v)} flow(e) - \sum_{e \in \delta^-(v)} flow(e) = 0, \quad \forall v \neq s, t \]

  1. \(s\) 流出的总流量 = 流入 \(t\) 的总流量 = 最大流值 \(F_{\max}\)(我们通常先求最大流,再在最大流中找最小费用;但网络单纯形通常同时进行)。

2. 核心思想

在单纯形法中,我们需要:

  • 一个基可行解(对应图中的一个生成树结构的流)
  • 一个检验数(reduced cost)来判断是否最优
  • 一个换基(pivot)操作来改进解

在最小费用流问题中:

  • 基变量对应生成树中的边
  • 非基变量对应非树边
  • 树边流量可以在其容量范围内自由调整(但通常取边界值:0 或容量上限,这称为可行树结构

3. 算法步骤(简化版本)

步骤1:构造初始可行树

我们需要一个可行树(spanning tree),包含所有顶点,并且树边的流量满足容量约束和流量守恒。
常用方法:

  1. 添加一条从汇点 \(t\) 到源点 \(s\) 的“人工边”,容量为无穷大,费用为一个大数 \(M\)(惩罚项),这样初始时所有流量可沿这条边循环,形成一个可行循环流,树由这条边和其它零流边组成。
  2. 或者,先用最大流算法求一个可行流,然后提取出支撑树。

步骤2:计算节点势(potentials)

对树结构,定义节点势 \(\pi(v)\) 使得对于每条树边 \(e = (u, v)\)

\[cost(e) = \pi(u) - \pi(v) \]

(因为树是连通的,可以从一个根(如 \(s\))开始 BFS/DFS 递推计算 \(\pi\),初始 \(\pi(s)=0\)

步骤3:计算非树边的检验数(reduced cost)

对非树边 \(e = (u, v)\),其检验数为:

\[\overline{c}(e) = cost(e) - (\pi(u) - \pi(v)) \]

在单纯形法中,如果所有非基变量(非树边)的检验数满足最优条件,则当前解最优。

最优条件(最小化问题):

  • 如果 \(flow(e) = 0\)(流量在下界),则检验数应 \(\geq 0\),否则增加流量可减少总费用。
  • 如果 \(flow(e) = cap(e)\)(流量在上界),则检验数应 \(\leq 0\),否则减少流量可减少总费用。
    (这是因为单纯法中的 reduced cost 符号规则)

步骤4:选取进基边

找一个不满足最优条件的非树边 \(e\)

  • \(flow(e)=0\)\(\overline{c}(e) < 0\),则增加流量可改进。
  • \(flow(e)=cap(e)\)\(\overline{c}(e) > 0\),则减少流量可改进。

步骤5:确定离基边

进基边 \(e\) 加入树中会形成一个(cycle)。

  • 如果要增加流量(对应 \(flow(e)=0\) 且检验数<0):在圈上沿与 \(e\) 同向的边上可增加流量,反向边上可减少流量。允许增加的最大流量受限于圈上反向边的最小剩余容量。
  • 如果要减少流量(对应 \(flow(e)=cap(e)\) 且检验数>0):在圈上沿与 \(e\) 反向的边上可增加流量,同向边上可减少流量。允许减少的最大流量受限于圈上同向边的最小流量。

确定最大可调整量 \(\delta\),并找到第一个达到界限的边 \(f\)(即调整 \(\delta\) 后,\(f\) 流量变为 0 或容量上限)。
\(f\) 移出树基,将 \(e\) 加入树基,更新树结构。

步骤6:更新流量和势

沿圈调整所有边的流量:同向边流量加 \(\delta\),反向边流量减 \(\delta\)
然后因为树结构改变,重新计算节点势 \(\pi\)(可以在原势基础上只更新子树,或完全重算)。

步骤7:重复

重复步骤3~6,直到所有非树边满足最优条件。此时得到最小费用最大流。


4. 算法复杂度与特点

  • 理论最坏情况可能是指数时间,但在实践中非常快,尤其适合稠密图和大规模问题。
  • 与连续最短路算法(Successive Shortest Path)相比,网络单纯形在每轮迭代中可能调整更多流量,收敛更快。
  • 需要仔细实现树结构的维护(可用父指针、深度、子树节点列表等)。

5. 举例说明(示意图思路)

假设有一个简单图:

  • 点:\(s, a, b, t\)
  • 边:
    \((s,a)\): cap=2, cost=1
    \((s,b)\): cap=1, cost=3
    \((a,b)\): cap=1, cost=1
    \((a,t)\): cap=2, cost=2
    \((b,t)\): cap=2, cost=1

初始可行树:选边 \((s,a),(a,b),(b,t)\) 和人工边 \((t,s)\) 构成树,初始流量全0。
第一次迭代:发现非树边 \((s,b)\) 检验数为负,加入树,形成圈 \(s \to b \to t \to s\),调整流量。
多次换基后,得到最大流3,最小费用 = 1*2 + 1*0 + 2*2 + 1*1 = 具体值(需计算)。


6. 总结

网络单纯形算法是求解最小费用最大流的强大工具,它将线性规划的单纯形法适配到网络流结构,通过维护一棵生成树和节点势,用检验数判断最优性,并通过换基改进解。虽然理论最坏复杂度不好,但实际性能优秀,被许多优化库采用(如LEMON、OR-Tools等)。

最小费用最大流问题的网络单纯形算法 题目描述 给定一个 有向图 ,每条边有 容量 (最多能通过多少流量)和 单位费用 (每单位流量通过该边所需的成本)。 有一个 源点 (source)和一个 汇点 (sink),问: 在不超过每条边容量的前提下,从源点发送尽可能多的流量到汇点,并且使得 总费用最小 。 这就是 最小费用最大流 (Minimum Cost Maximum Flow, MCMF)问题。 网络单纯形算法是求解该问题的一种高效方法,它结合了 单纯形法 (线性规划)与 图的结构特性 。 解题思路 网络单纯形算法可以看作 单纯形法 在最小费用流问题上的特化版本,它直接在图上操作,利用 生成树结构 来维护一个可行流,并不断进行 换基操作 (pivot)来降低费用,直到达到最优。 1. 问题形式化 设图 \(G = (V, E)\),每条边 \(e = (u, v)\) 有: 容量 \(cap(e) \geq 0\) 单位费用 \(cost(e)\)(可正可负) 流量 \(flow(e)\)(待求变量) 我们要最小化总费用: \[ \min \sum_ {e \in E} cost(e) \cdot flow(e) \] 约束条件: 容量约束:\(0 \leq flow(e) \leq cap(e)\) 流量守恒(除源点 \(s\) 和汇点 \(t\)): \[ \sum_ {e \in \delta^+(v)} flow(e) - \sum_ {e \in \delta^-(v)} flow(e) = 0, \quad \forall v \neq s, t \] 从 \(s\) 流出的总流量 = 流入 \(t\) 的总流量 = 最大流值 \(F_ {\max}\)(我们通常先求最大流,再在最大流中找最小费用;但网络单纯形通常同时进行)。 2. 核心思想 在单纯形法中,我们需要: 一个 基可行解 (对应图中的一个 生成树结构 的流) 一个 检验数 (reduced cost)来判断是否最优 一个 换基 (pivot)操作来改进解 在最小费用流问题中: 基变量对应 生成树 中的边 非基变量对应 非树边 树边流量可以在其容量范围内自由调整(但通常取边界值:0 或容量上限,这称为 可行树结构 ) 3. 算法步骤(简化版本) 步骤1:构造初始可行树 我们需要一个 可行树 (spanning tree),包含所有顶点,并且树边的流量满足容量约束和流量守恒。 常用方法: 添加一条从汇点 \(t\) 到源点 \(s\) 的“人工边”,容量为无穷大,费用为一个大数 \(M\)(惩罚项),这样初始时所有流量可沿这条边循环,形成一个可行循环流,树由这条边和其它零流边组成。 或者,先用最大流算法求一个可行流,然后提取出支撑树。 步骤2:计算节点势(potentials) 对树结构,定义节点势 \(\pi(v)\) 使得对于每条树边 \(e = (u, v)\): \[ cost(e) = \pi(u) - \pi(v) \] (因为树是连通的,可以从一个根(如 \(s\))开始 BFS/DFS 递推计算 \(\pi\),初始 \(\pi(s)=0\)) 步骤3:计算非树边的检验数(reduced cost) 对非树边 \(e = (u, v)\),其检验数为: \[ \overline{c}(e) = cost(e) - (\pi(u) - \pi(v)) \] 在单纯形法中,如果所有非基变量(非树边)的检验数满足最优条件,则当前解最优。 最优条件 (最小化问题): 如果 \(flow(e) = 0\)(流量在下界),则检验数应 \(\geq 0\),否则增加流量可减少总费用。 如果 \(flow(e) = cap(e)\)(流量在上界),则检验数应 \(\leq 0\),否则减少流量可减少总费用。 (这是因为单纯法中的 reduced cost 符号规则) 步骤4:选取进基边 找一个不满足最优条件的非树边 \(e\): 若 \(flow(e)=0\) 且 \(\overline{c}(e) < 0\),则增加流量可改进。 若 \(flow(e)=cap(e)\) 且 \(\overline{c}(e) > 0\),则减少流量可改进。 步骤5:确定离基边 进基边 \(e\) 加入树中会形成一个 圈 (cycle)。 如果要增加流量(对应 \(flow(e)=0\) 且检验数 <0):在圈上沿与 \(e\) 同向的边上可增加流量,反向边上可减少流量。允许增加的最大流量受限于圈上反向边的最小剩余容量。 如果要减少流量(对应 \(flow(e)=cap(e)\) 且检验数>0):在圈上沿与 \(e\) 反向的边上可增加流量,同向边上可减少流量。允许减少的最大流量受限于圈上同向边的最小流量。 确定最大可调整量 \(\delta\),并找到第一个达到界限的边 \(f\)(即调整 \(\delta\) 后,\(f\) 流量变为 0 或容量上限)。 将 \(f\) 移出树基,将 \(e\) 加入树基,更新树结构。 步骤6:更新流量和势 沿圈调整所有边的流量:同向边流量加 \(\delta\),反向边流量减 \(\delta\)。 然后因为树结构改变,重新计算节点势 \(\pi\)(可以在原势基础上只更新子树,或完全重算)。 步骤7:重复 重复步骤3~6,直到所有非树边满足最优条件。此时得到最小费用最大流。 4. 算法复杂度与特点 理论最坏情况可能是指数时间,但在实践中非常快,尤其适合稠密图和大规模问题。 与连续最短路算法(Successive Shortest Path)相比,网络单纯形在每轮迭代中可能调整更多流量,收敛更快。 需要仔细实现树结构的维护(可用父指针、深度、子树节点列表等)。 5. 举例说明(示意图思路) 假设有一个简单图: 点:\(s, a, b, t\) 边: \((s,a)\): cap=2, cost=1 \((s,b)\): cap=1, cost=3 \((a,b)\): cap=1, cost=1 \((a,t)\): cap=2, cost=2 \((b,t)\): cap=2, cost=1 初始可行树:选边 \((s,a),(a,b),(b,t)\) 和人工边 \((t,s)\) 构成树,初始流量全0。 第一次迭代:发现非树边 \((s,b)\) 检验数为负,加入树,形成圈 \(s \to b \to t \to s\),调整流量。 多次换基后,得到最大流3,最小费用 = 1\*2 + 1\*0 + 2\*2 + 1\*1 = 具体值(需计算)。 6. 总结 网络单纯形算法是求解最小费用最大流的强大工具,它将线性规划的单纯形法适配到网络流结构,通过维护一棵生成树和节点势,用检验数判断最优性,并通过换基改进解。虽然理论最坏复杂度不好,但实际性能优秀,被许多优化库采用(如LEMON、OR-Tools等)。