最小斯坦纳树(Steiner Tree)问题的精确解法(Dreyfus-Wagner 算法)
字数 2578 2025-12-22 00:03:40

最小斯坦纳树(Steiner Tree)问题的精确解法(Dreyfus-Wagner 算法)


题目描述

给定一个无向带权连通图 \(G = (V, E)\),其中每条边有非负权重,以及一个终端顶点集合 \(T \subseteq V\)(称为 Steiner points)。
目标:找到一个总权重最小的连通子图 \(S \subseteq G\),使得 \(T\) 中的所有顶点都在 \(S\) 中连通。
这个子图 \(S\) 被称为最小斯坦纳树(Minimum Steiner Tree)。
允许使用 \(V \setminus T\) 中的顶点(称为 Steiner 顶点)来降低总权重。


解题过程

这是一个经典的NP-hard问题,但终端集合 \(|T| = k\) 较小时,可以用动态规划\(O(3^k n + 2^k n^2)\) 时间内精确求解,这就是 Dreyfus-Wagner 算法

步骤 1:问题转换

斯坦纳树不一定是树,但最优解一定是树(否则可以删去环上一条边,总权重下降),所以我们只需找树。

设:

  • \(n = |V|\)
  • \(k = |T|\),终端集合 \(T = \{t_1, t_2, \dots, t_k\}\)
  • 顶点编号 \(1, 2, \dots, n\)

目标:找到连接 \(T\) 的最小权重树。


步骤 2:子问题定义

核心思路:以集合划分的方式合并部分解

定义动态规划状态:

  1. \(dp[S][v]\)
    其中 \(S \subseteq T\) 是一个终端子集,\(v \in V\)
    含义:连接集合 \(S\) 中的所有终端,并且以顶点 \(v\) 为根的最小权重树的权重。
    注意:这棵树必须包含 \(v\),且 \(v\) 不一定在 \(S\) 中,但 \(S\) 中所有顶点必须连通到 \(v\)

最终答案:

\[\min_{v \in V} dp[T][v] \]

因为以任意顶点为根都可以,最后取最小值。


步骤 3:状态转移

转移 1:合并两棵子树
对于状态 \(dp[S][v]\)\(|S| \ge 2\),可以\(S\) 划分成两个非空子集 \(S_1\)\(S_2\),分别用两棵子树连接它们,然后在 \(v\) 处汇合:

\[dp[S][v] = \min_{\substack{\emptyset \neq S_1 \subset S \\ S_2 = S \setminus S_1}} \left( dp[S_1][v] + dp[S_2][v] \right) \]

这个式子表示:分别构建连接 \(S_1\)\(S_2\) 的树,两棵树都以 \(v\) 为根,合起来就是连接 \(S\) 的树(两棵树在 \(v\) 处合并)。

转移 2:通过另一顶点中转
对于 \(dp[S][v]\),可以考虑先用一棵树连接 \(S\) 到某个其他顶点 \(u\)\(u\) 可能作为 Steiner 顶点),然后再用 \(v\)\(u\) 的最短路径连接 \(v\) 和这棵树:

\[dp[S][v] = \min_{u \in V} \left( dp[S][u] + dist(u, v) \right) \]

其中 \(dist(u, v)\) 是图中 \(u\)\(v\)最短路径长度


步骤 4:动态规划顺序

我们需要先计算所有顶点对之间的最短路径 \(dist(u, v)\)(用 Floyd-Warshall 或 \(k\) 次 Dijkstra,因为 \(n\) 可能不大)。

然后按 \(|S|\) 从小到大的顺序计算 \(dp[S][v]\)

  1. 初始化:
    \(S = \{t\}\) 是单元素集合时,只需连接到 \(v\) 的最小权重就是 \(dist(t, v)\)

\[ dp[\{t\}][v] = dist(t, v) \]

  1. 对于 \(|S| \ge 2\)
    • 先用转移 1 的合并方式计算一个初始值(枚举 \(S\) 的所有非平凡划分)。
    • 再用转移 2 的中转方式优化:对每个 \(v\),尝试所有 \(u\) 看是否可以通过 \(u\) 得到更小的权重。

注意:转移 2 类似最短路的松弛,可以迭代直到不再更新。


步骤 5:算法伪代码

1. 计算全源最短路径 dist[1..n][1..n](O(n^3) 或 O(n^2 log n) 等)。
2. 初始化 dp[S][v] = ∞ 对所有 S ⊆ T, v ∈ V。
3. 对每个终端 t ∈ T 和每个 v ∈ V:
      dp[{t}][v] = dist[t][v]。
4. 对大小 |S| 从 2 到 k:
     对于每个 S ⊆ T 且 |S| = s:
         // 步骤 A:合并
         对于每个非空真子集 S1 ⊂ S:
            S2 = S - S1
            对于每个 v ∈ V:
                dp[S][v] = min(dp[S][v], dp[S1][v] + dp[S2][v])
         // 步骤 B:中转松弛(类似最短路)
         重复 n 次(或直到收敛):
            对于每个 v ∈ V:
               对于每个 u ∈ V:
                  dp[S][v] = min(dp[S][v], dp[S][u] + dist[u][v])
5. 答案 = min_{v ∈ V} dp[T][v]。

步骤 6:时间与空间复杂度

  • 状态数:\(2^k n\)(每个 S ⊆ T 和每个 v ∈ V)。
  • 转移:
    • 合并转移:对每个 S,枚举子集 S1 有 \(2^{|S|}\) 种,但可以在所有大小上平摊到 \(3^k\) 种划分(因为每个元素在 S1、S2 或不在 S 中)。
    • 中转转移:对每个 S 做 n 轮松弛,每轮 O(n^2)(但实际常一次松弛即可收敛)。
  • 总复杂度:\(O(3^k n + 2^k n^2)\)
  • 空间:\(O(2^k n)\)

步骤 7:举例说明

假设图有 4 个顶点:\(V=\{1,2,3,4\}\),边权如图,终端 \(T = \{1,2,3\}\)

  1. 先算全源最短路 dist。
  2. 初始化:
    • \(dp[\{1\}][1]=0, dp[\{1\}][2]=dist(1,2), \dots\)
  3. 计算 \(S=\{1,2\}\)
    • 合并:\(dp[\{1\}][v] + dp[\{2\}][v]\) 取最小。
    • 中转:尝试用顶点 4 中转看是否更优。
  4. 最终 \(dp[\{1,2,3\}][v]\) 的最小值就是答案。

小结

Dreyfus-Wagner 算法的核心是集合划分动态规划,利用终端集合较小(k ≤ 20 左右)时可行,是精确求解小规模斯坦纳树问题的标准方法。

最小斯坦纳树(Steiner Tree)问题的精确解法(Dreyfus-Wagner 算法) 题目描述 给定一个 无向带权连通图 \( G = (V, E) \),其中每条边有非负权重,以及一个 终端顶点集合 \( T \subseteq V \)(称为 Steiner points )。 目标:找到一个 总权重最小 的连通子图 \( S \subseteq G \),使得 \( T \) 中的所有顶点都在 \( S \) 中连通。 这个子图 \( S \) 被称为 最小斯坦纳树 (Minimum Steiner Tree)。 允许使用 \( V \setminus T \) 中的顶点(称为 Steiner 顶点 )来降低总权重。 解题过程 这是一个经典的 NP-hard 问题,但终端集合 \( |T| = k \) 较小时,可以用 动态规划 在 \( O(3^k n + 2^k n^2) \) 时间内精确求解,这就是 Dreyfus-Wagner 算法 。 步骤 1:问题转换 斯坦纳树不一定是树,但最优解一定是树(否则可以删去环上一条边,总权重下降),所以我们只需找树。 设: \( n = |V| \) \( k = |T| \),终端集合 \( T = \{t_ 1, t_ 2, \dots, t_ k\} \) 顶点编号 \( 1, 2, \dots, n \) 目标:找到连接 \( T \) 的最小权重树。 步骤 2:子问题定义 核心思路: 以集合划分的方式合并部分解 。 定义动态规划状态: \( dp[ S][ v ] \): 其中 \( S \subseteq T \) 是一个终端子集,\( v \in V \)。 含义:连接集合 \( S \) 中的所有终端,并且以顶点 \( v \) 为根的 最小权重树 的权重。 注意:这棵树必须包含 \( v \),且 \( v \) 不一定在 \( S \) 中,但 \( S \) 中所有顶点必须连通到 \( v \)。 最终答案: \[ \min_ {v \in V} dp[ T][ v ] \] 因为以任意顶点为根都可以,最后取最小值。 步骤 3:状态转移 转移 1:合并两棵子树 对于状态 \( dp[ S][ v] \) 且 \( |S| \ge 2 \),可以 将 \( S \) 划分成两个非空子集 \( S_ 1 \) 和 \( S_ 2 \) ,分别用两棵子树连接它们,然后在 \( v \) 处汇合: \[ dp[ S][ v] = \min_ {\substack{\emptyset \neq S_ 1 \subset S \\ S_ 2 = S \setminus S_ 1}} \left( dp[ S_ 1][ v] + dp[ S_ 2][ v ] \right) \] 这个式子表示:分别构建连接 \( S_ 1 \) 和 \( S_ 2 \) 的树,两棵树都以 \( v \) 为根,合起来就是连接 \( S \) 的树(两棵树在 \( v \) 处合并)。 转移 2:通过另一顶点中转 对于 \( dp[ S][ v ] \),可以考虑先用一棵树连接 \( S \) 到某个其他顶点 \( u \)(\( u \) 可能作为 Steiner 顶点),然后再用 \( v \) 到 \( u \) 的最短路径连接 \( v \) 和这棵树: \[ dp[ S][ v] = \min_ {u \in V} \left( dp[ S][ u ] + dist(u, v) \right) \] 其中 \( dist(u, v) \) 是图中 \( u \) 到 \( v \) 的 最短路径长度 。 步骤 4:动态规划顺序 我们需要先计算所有顶点对之间的最短路径 \( dist(u, v) \)(用 Floyd-Warshall 或 \( k \) 次 Dijkstra,因为 \( n \) 可能不大)。 然后按 \( |S| \) 从小到大的顺序计算 \( dp[ S][ v ] \): 初始化: 当 \( S = \{t\} \) 是单元素集合时,只需连接到 \( v \) 的最小权重就是 \( dist(t, v) \): \[ dp[ \{t\}][ v ] = dist(t, v) \] 对于 \( |S| \ge 2 \): 先用 转移 1 的合并方式计算一个初始值(枚举 \( S \) 的所有非平凡划分)。 再用 转移 2 的中转方式优化:对每个 \( v \),尝试所有 \( u \) 看是否可以通过 \( u \) 得到更小的权重。 注意:转移 2 类似最短路的松弛,可以迭代直到不再更新。 步骤 5:算法伪代码 步骤 6:时间与空间复杂度 状态数:\( 2^k n \)(每个 S ⊆ T 和每个 v ∈ V)。 转移: 合并转移:对每个 S,枚举子集 S1 有 \( 2^{|S|} \) 种,但可以在所有大小上平摊到 \( 3^k \) 种划分(因为每个元素在 S1、S2 或不在 S 中)。 中转转移:对每个 S 做 n 轮松弛,每轮 O(n^2)(但实际常一次松弛即可收敛)。 总复杂度:\( O(3^k n + 2^k n^2) \)。 空间:\( O(2^k n) \)。 步骤 7:举例说明 假设图有 4 个顶点:\( V=\{1,2,3,4\} \),边权如图,终端 \( T = \{1,2,3\} \)。 先算全源最短路 dist。 初始化: \( dp[ \{1\}][ 1]=0, dp[ \{1\}][ 2 ]=dist(1,2), \dots \) 计算 \( S=\{1,2\} \): 合并:\( dp[ \{1\}][ v] + dp[ \{2\}][ v ] \) 取最小。 中转:尝试用顶点 4 中转看是否更优。 最终 \( dp[ \{1,2,3\}][ v ] \) 的最小值就是答案。 小结 Dreyfus-Wagner 算法的核心是 集合划分动态规划 ,利用终端集合较小(k ≤ 20 左右)时可行,是精确求解小规模斯坦纳树问题的标准方法。