基于线性规划的图最大流问题中流量分解与流路分解的算法示例
1. 问题描述
给定一个有向图 \(G = (V, E)\),每条边 \(e \in E\) 有一个非负容量 \(c(e)\)。图中有一个指定的源点 \(s \in V\) 和一个指定的汇点 \(t \in V\)。假设我们已经通过某种最大流算法(如 Ford-Fulkerson、Edmonds-Karp、Dinic 或 Push-Relabel 算法)求得了该网络的一个可行流 \(f: E \to \mathbb{R}^+\),且 \(f\) 是一个从 \(s\) 到 \(t\) 的最大流。
流量分解(Flow Decomposition) 的目标是:将任意一个可行流 \(f\) 分解成若干条从 \(s\) 到 \(t\) 的路径流(以及可能存在的环流),使得每条路径或环上的流量非负,且这些流量的总和恰好等于原流 \(f\) 在每条边上的流量。
流路分解(Path Decomposition) 是流量分解的一个特例,它要求分解结果仅由从 \(s\) 到 \(t\) 的路径组成(忽略环流),且每条路径上的流量为正。
在最大流应用中,流量分解常用于:
- 分析网络流的结构;
- 提取实际的流路径(如数据传输路径、交通流路径);
- 为多商品流、流量分配等问题提供基础。
2. 基本思路
对于一个可行流 \(f\),我们可以通过以下步骤进行流量分解:
- 消除环流:通过寻找并消除流中的环,将流转化为一个无环流(即不存在流量构成的环)。
- 提取 \(s \to t\) 路径:从无环流中不断提取从 \(s\) 到 \(t\) 的路径,并分配流量,直到流值降为零。
由于最大流算法通常不显式给出路径,分解过程需要基于流的边流量值进行。
3. 核心算法步骤
步骤 1:建立辅助数据结构
令当前流为 \(f\)。我们维护每个顶点的净流出量:
\[\text{net-out}(v) = \sum_{(v,u) \in E} f(v,u) - \sum_{(u,v) \in E} f(u,v) \]
对于可行流,除源点 \(s\) 和汇点 \(t\) 外,其他顶点均满足流量守恒:\(\text{net-out}(v) = 0\)。对于 \(s\) 和 \(t\),有:
\[\text{net-out}(s) = F, \quad \text{net-out}(t) = -F \]
其中 \(F\) 是流的值。
步骤 2:消除环流
目标:将流 \(f\) 转化为一个等价的无环流(即不存在边流量全为正的环)。
- 在 \(f\) 的正流量边(即 \(f(e) > 0\) 的边)组成的子图中,使用深度优先搜索(DFS)寻找环。
- 若找到一个环 \(C\),设环上边的最小流量为 \(\delta = \min_{e \in C} f(e)\)。
- 将环上每条边的流量减少 \(\delta\),这不会改变任何顶点的净流出量,因此流仍然是可行的。
- 重复直到图中无环。
正确性:每次消环至少使一条边的流量降为零,因此算法在 \(O(m \cdot F)\) 时间内结束(\(m\) 为边数),但若使用更高效的方法(如拓扑排序配合调整),可在 \(O(mn)\) 内完成。
步骤 3:提取 \(s \to t\) 路径
在无环流中,可以不断执行以下操作:
- 从 \(s\) 出发,沿着任意一条流量为正的边前进。
- 由于图无环且除 \(s\) 和 \(t\) 外流量守恒,最终一定能到达 \(t\),形成一条路径 \(P\)。
- 设路径 \(P\) 上的最小流量为 \(\delta = \min_{e \in P} f(e)\)。
- 将 \(P\) 加入分解结果,并分配流量 \(\delta\)。
- 将 \(P\) 上每条边的流量减少 \(\delta\)(如果某边流量降为零,则从当前流中移除该边)。
- 更新 \(s\) 和 \(t\) 的净流出量:\(\text{net-out}(s) := \text{net-out}(s) - \delta\),\(\text{net-out}(t) := \text{net-out}(t) + \delta\)。
重复以上过程,直到 \(\text{net-out}(s) = 0\)。
步骤 4:算法终止
当 \(\text{net-out}(s) = 0\) 时,所有从 \(s\) 到 \(t\) 的流量已被分解为若干条路径及其流量。每条路径 \(P\) 对应一个流量值 \(\delta_P\),满足:
\[\sum_{P: s \leadsto t} \delta_P = F \]
且对于每条边 \(e\):
\[f(e) = \sum_{P: e \in P} \delta_P \]
4. 举例说明
考虑以下有向图(边上的数字表示容量 \(c\) / 当前流 \(f\) ):
(a)
/ \
10/5 \5/5
/ \
(s)-----(t)
\ 10/5
\ /
(b)
边列表:
- \(s \to a\): 容量 10,流 5
- \(s \to b\): 容量 10,流 5
- \(a \to t\): 容量 5,流 5
- \(b \to t\): 容量 5,流 5
- \(a \to b\): 容量 5,流 0
流值 \(F = 10\)。
分解过程:
- 该流已经是无环的(没有正流量的环)。
- 从 \(s\) 出发:
- 路径1: \(s \to a \to t\),最小流量 \(\delta_1 = 5\)。
更新后:\(s \to a\) 流降为0,\(a \to t\) 流降为0。 - 路径2: \(s \to b \to t\),最小流量 \(\delta_2 = 5\)。
更新后:\(s \to b\) 流降为0,\(b \to t\) 流降为0。
- 路径1: \(s \to a \to t\),最小流量 \(\delta_1 = 5\)。
- 流值已降为0,分解结束。
分解结果:
- 路径 \(s-a-t\) 流量 5
- 路径 \(s-b-t\) 流量 5
5. 算法复杂度
- 消环部分:使用拓扑排序检测环并调整,可在 \(O(mn)\) 时间内完成。
- 路径提取部分:每次提取一条路径需要 \(O(n)\) 时间,最多有 \(F\) 条路径(若流量为整数),但若使用最小流量提取,则路径数不超过 \(m\) 条。因此路径提取可在 \(O(mn)\) 时间内完成。
- 总复杂度:\(O(mn)\)。
6. 实际应用意义
- 流量分析:将抽象的最大流转化为具体的路径,便于理解网络的使用情况。
- 多路径路由:在通信网络中,最大流分解可用于分配流量到多条路径,实现负载均衡。
- 算法辅助:在列生成算法中,分解得到的路径可作为候选列加入主问题。
7. 扩展
- 分数流与整数流:若容量和流为整数,分解得到的路径流量也为整数。
- 环流处理:若允许环流存在,分解结果可能包含环,但通常环流对 \(s \to t\) 的流量无贡献,可忽略。
- 最小路径数分解:若希望用最少路径表示流,可转化为最小费用流问题,每次提取一条最大可能流量的路径。
通过以上步骤,我们可以将任何最大流分解为若干条从源到汇的路径及其流量,从而为网络流分析提供直观的结构信息。