无向图的最小费用最大循环流问题
题目描述
给定一个无向连通图 \(G = (V, E)\),其中每条边 \(e \in E\) 有两个属性:容量 \(c(e) > 0\)(表示该边最多能承载的流量)和单位费用 \(w(e)\)(可正可负,表示沿该边输送一单位流量所产生的成本)。图中没有源点和汇点,也不要求流量守恒。我们的目标是:在图中找到一个“循环流”(即每条边 \(e\) 有一个方向,我们为其分配一个非负流量 \(f(e)\),且 \(f(e) \le c(e)\)),并最小化所有边上的总费用,即 \(\sum_{e \in E} w(e) \cdot f(e)\)。 由于没有流量守恒约束,此问题可以建模为在容量约束下,寻找一个使总费用最小的有向循环流。
解题过程
这个问题的关键是将无向图转化为一个有向图网络,使得我们能够利用现有算法(如负权环检测、最小费用流)来求解。下面分步骤讲解。
步骤1:问题转化——将无向边转化为两条有向边
在传统的有向图网络流问题中,每条有向边有明确的容量和费用。但这里我们的图是无向的。为了应用已知算法,我们将每条无向边 \(e = (u, v)\) 拆分为两条方向相反的有向边:
- 一条从 \(u\) 到 \(v\) 的边 \(e_1\),容量为 \(c(e)\),费用为 \(w(e)\)。
- 一条从 \(v\) 到 \(u\) 的边 \(e_2\),容量为 \(c(e)\),费用为 \(w(e)\)。
为什么这样做?因为在实际的循环流中,流量可以从 \(u\) 流向 \(v\),也可以从 \(v\) 流向 \(u\),但不能同时双向流动(因为我们最终要赋予每条边一个方向和一个流量值)。不过,我们暂时先允许两条方向边都承载流量,稍后会通过约束保证不会出现双向流动的情况。
现在,我们得到一个有向图 \(D = (V, A)\),其中 \(|A| = 2|E|\)。每条弧 \(a \in A\) 有容量 \(c(a)\) 和费用 \(w(a)\)。目标是找到一个流 \(f: A \to \mathbb{R}_{\ge 0}\),满足:
- \(0 \le f(a) \le c(a)\) 对每条弧 \(a \in A\)(容量约束)。
- 最小化总费用 \(\sum_{a \in A} w(a) \cdot f(a)\)。
注意:这个有向图仍然没有流量守恒约束(即节点的净流入不必为零),所以它是一个非常宽松的模型。
步骤2:引入“不允许双向流动”约束
在原问题中,每条无向边只能选择一个方向来承载流量。但在我们当前的有向图模型中,一条无向边对应的两条反向弧可能同时有正流量(即同时有从 \(u\) 到 \(v\) 和从 \(v\) 到 \(u\) 的流量),这相当于无向边内部有“循环”,会导致无意义的流量和增加不必要的费用。
我们需要添加约束:对于每条原无向边 \(e = (u, v)\) 对应的两条弧 \(a_{uv}\) 和 \(a_{vu}\),必须满足 \(f(a_{uv}) \cdot f(a_{vu}) = 0\),即至少有一个为0。这等价于说,这两条弧上的流量之和不能超过容量 \(c(e)\),且不能两条同时为正。
一个巧妙的处理方法是:将容量约束改为:对于每条无向边 \(e\),两条反向弧的流量之和不超过 \(c(e)\),即 \(f(a_{uv}) + f(a_{vu}) \le c(e)\)。这样,如果两条弧都为正,其和不超过容量,但可能会违反“不能双向”的原则吗?实际上,如果费用为正,同时分配双向流量只会增加总费用,不是最优解;如果费用为负,同时分配双向流量会减少总费用,但我们可以通过检查是否存在负费用有向环来避免这种情况。
步骤3:最优解的结构与负环检测
在没有流量守恒约束、只有容量约束的流问题中,最优解的结构很简单:
- 如果一条边的费用 \(w(e) > 0\),我们尽量分配 0 流量 以最小化成本。
- 如果一条边的费用 \(w(e) < 0\),我们尽量分配 最大容量 \(c(e)\) 的流量,因为负费用意味着“输送流量能赚钱”,我们想尽可能多输送。
- 但要注意:我们不能独立决定每条边,因为流量必须形成一个循环(即最终分配给每条边一个方向,并满足图作为一个整体是循环的)。实际上,我们可以证明:最优解一定可以表示为若干个有向环的叠加,每个环上的边方向一致,且环上每条边要么流量为0,要么为满容量。
更形式化地,我们可以将问题重述为:
寻找一个集合的有向环,每个环有一个流量值,环上的边方向一致,且每个环的流量乘以环上边的费用之和为负(否则加入该环会增加总费用)。我们想尽可能多地输送负费用环上的流量,直到某条边达到容量上限。
因此,算法思路是:
- 从零流开始。
- 在有向图 \(D\) 中,寻找一个负费用有向环(即环上所有边的费用之和为负)。
- 如果能找到这样的环,就尽可能多地增加该环上的流量(增加量受环上最小剩余容量限制)。
- 重复步骤2-3,直到找不到负费用有向环为止。此时得到最小费用循环流。
为什么?因为增加一个负费用环的流量会降低总费用,而无法再找到负费用环时,说明已达到局部最优(实际上也是全局最优,类似于最小费用流中的“负环最优性定理”)。
步骤4:算法实现细节
我们可以用 Bellman-Ford 算法(或其改进版本)来检测负环。但需要注意:我们的图允许负权边,且没有源点,所以不能直接运行单源最短路径算法。常用的方法是:
- 在图中添加一个超级源点 \(s\),从 \(s\) 向所有其他节点连一条费用为0、容量为无穷大的边。
- 以 \(s\) 为源点,用 SPFA(Shortest Path Faster Algorithm)或带负环检测的 Bellman-Ford 计算最短路径距离 \(dist[v]\)。
- 根据距离 \(dist[v]\) 调整边的费用(类似于最小费用流中的“势能”方法),得到非负费用的图,然后在此图上找负环? 等等,这里有点微妙。
实际上,更直接的方法是:
- 我们维护当前流 \(f\)。
- 根据 \(f\) 定义残量网络:每条弧 \(a\) 如果 \(f(a) < c(a)\),则保留其原有方向,剩余容量为 \(c(a) - f(a)\),费用为 \(w(a)\);如果 \(f(a) > 0\),则添加一条反向弧,容量为 \(f(a)\),费用为 \(-w(a)\)(表示退流可减少费用)。
- 在残量网络中找负费用有向环。若找到,则沿环增广。
步骤5:具体步骤举例
假设有一个无向三角形图,顶点 \(\{1, 2, 3\}\),三条边:
- \((1,2): c=3, w=2\)(正费用)
- \((2,3): c=4, w=-1\)(负费用)
- \((3,1): c=2, w=1\)(正费用)
步骤5.1 转化为有向图:
每条无向边变成两条有向弧,共6条弧,容量分别为3,3,4,4,2,2,费用分别为2,2,-1,-1,1,1。
步骤5.2 初始流 \(f = 0\)。
步骤5.3 构建初始残量网络:就是原图的所有弧,剩余容量等于原容量,费用不变。
步骤5.4 找负费用环:
环 \(2 \to 3 \to 1 \to 2\)(对应弧 \((2,3)\), \((3,1)\), \((1,2)\) 方向)费用和为 \((-1) + 1 + 2 = 2 > 0\),不是负环。
环 \(2 \to 1 \to 3 \to 2\)(对应弧 \((2,1)\), \((1,3)\), \((3,2)\) 方向)费用和为 \(2 + 1 + (-1) = 2 > 0\),也不是负环。
环 \(1 \to 3 \to 2 \to 1\)(对应弧 \((1,3)\), \((3,2)\), \((2,1)\) 方向)费用和为 \(1 + (-1) + 2 = 2 > 0\)。
看起来没有负环?等等,我们忽略了一个简单环: \(2 \to 3\) 和 \(3 \to 2\) 是一个长度为2的环,但它们是同一条无向边的反向弧,在原问题中不允许同时为正。不过,在我们的残量网络中,允许这样的“双向”环吗?如果我们尝试增加 \((2,3)\) 和 \((3,2)\) 上的流量,实际上相当于在原无向边 \((2,3)\) 上同时分配双向流量,这虽然可能降低总费用(因为费用为负),但违反原问题约束。所以,我们必须在构建残量网络时,避免出现这种“同一条无向边两条弧都在环中”的情况。
因此,我们需要修改残量网络的定义:对于每条无向边 \(e = (u,v)\) 对应的两条弧 \(a_{uv}\) 和 \(a_{vu}\),如果当前 \(f(a_{uv}) + f(a_{vu}) < c(e)\),则可以选择其中一条弧增加流量(但不能同时选择两条)。这可以通过在找环时添加约束来实现,但实现起来较复杂。
实际上,更常见的做法是:将无向边看作两个节点之间的一条“可双向流动”的边,但流量净值为 \(f_{uv} - f_{vu}\),且要求 \(|f_{uv} - f_{vu}| \le c(e)\)。这样,我们可以将问题转化为一个带上下界的网络流问题,但这里不展开。
对于本例,直观最优解是:在负费用边 \((2,3)\) 上尽可能多输送流量,但由于需要形成循环,它必须和另外两条边一起构成环。实际上,环 \(1 \to 2 \to 3 \to 1\) 上,边 \((1,2)\) 和 \((3,1)\) 费用为正,所以输送流量会增加成本;因此,最优解可能就是零流,总费用为0。
步骤6:算法总结
- 输入:无向图 \(G=(V,E)\),每条边容量 \(c(e)\),费用 \(w(e)\)。
- 转化:构建有向图 \(D=(V,A)\),每条无向边对应两条反向弧,容量均为 \(c(e)\),费用均为 \(w(e)\)。
- 初始化:流 \(f = 0\)。
- 重复:
- 构建残量网络:对每条弧 \(a \in A\),如果 \(f(a) < c(a)\),则保留正向弧,剩余容量 \(c(a)-f(a)\),费用 \(w(a)\);如果 \(f(a) > 0\),则添加反向弧,容量 \(f(a)\),费用 \(-w(a)\)。
- 在残量网络中,运行 Bellman-Ford 或 SPFA 检测是否存在负费用有向环(注意:要避免检测到长度为2的“双向弧环”,这可通过标记每条无向边,确保环中不会同时包含同一无向边的两条弧)。
- 如果找到负费用环 \(C\),则计算环上最小剩余容量 \(\Delta = \min_{a \in C} residual\_capacity(a)\)。然后,沿环增加流量 \(\Delta\)(正向弧流量增加 \(\Delta\),反向弧流量减少 \(\Delta\))。
- 直到残量网络中无负费用环。
- 输出:最终流 \(f\),总费用 \(\sum_{a \in A} w(a) \cdot f(a)\)。
复杂度:每次找负环需 \(O(|V||A|)\) 时间,最多增广 \(O(|V| \cdot \max c(e))\) 次(如果容量为整数),但通常用 scaling 技术优化。实践中,该算法在中小规模图上可行。
通过以上步骤,我们逐步将无向图的最小费用最大循环流问题,转化为有向图的负环检测与增广问题,从而得到最优解。