最小斯坦纳树(Steiner Tree)问题
题目描述
给定一个无向连通图 \(G = (V, E)\) ,每条边有一个正权重(表示距离或成本),以及一个必须连接的顶点子集 \(S \subseteq V\)(称为终端点,terminal vertices)。
最小斯坦纳树是 \(G\) 中一个包含 \(S\) 中所有顶点的连通子图,且总边权最小。这个子图不一定是生成树(因为它可以包含 \(S\) 之外的顶点,这些顶点称为斯坦纳点)。
问题分析
- 如果 \(S = V\) ,就是最小生成树问题,可以用 Kruskal 或 Prim 算法解决。
- 如果 \(S\) 只有 2 个点,就是最短路径问题,可以用 Dijkstra 算法解决。
- 当 \(2 < |S| < |V|\) 时,问题变为 NP-hard。我们通常用动态规划来求精确解,其中经典方法是 Dreyfus-Wagner 算法。
解题思路(Dreyfus-Wagner 算法)
1. 动态规划状态定义
设 \(dp[u][T]\) 表示:以顶点 \(u\) 为根,连接了终端点集合 \(T \subseteq S\) 的所有顶点的最小斯坦纳树的总权重。
- \(u\) 可以是任何顶点( \(u \in V\) ),不一定是终端点。
- \(T\) 是 \(S\) 的子集。
- 最终答案 = \(\min_{u \in V} dp[u][S]\)。
2. 状态转移方程
转移分为两步:
(1) 第一部分:合并两个以 \(u\) 为根的子树
如果已经有两个以 \(u\) 为根的子树,分别连接终端子集 \(T_1\) 和 \(T_2\)(且 \(T_1 \cap T_2 = \emptyset\), \(T_1 \cup T_2 = T\) ),则合并后仍以 \(u\) 为根,连接 \(T\) 的所有终端点。
转移方程:
\[dp[u][T] = \min_{T' \subset T} \left\{ dp[u][T'] + dp[u][T \setminus T'] \right\} \]
这里枚举 \(T\) 的所有非空真子集 \(T'\),将两个以 \(u\) 为根的子树合并(它们在 \(u\) 处相连,公共点 \(u\) 不重复计算边权,直接相加权重即可)。
(2) 第二部分:换根
考虑从另一个顶点 \(v\) 连接到 \(u\) 的情况。
即:先构造以 \(v\) 为根、连接终端集 \(T\) 的树,然后从 \(v\) 走到 \(u\)(经过最短路径),将 \(u\) 作为新根。
转移方程:
\[dp[u][T] = \min_{v \in V} \left\{ dp[v][T] + dist(u, v) \right\} \]
其中 \(dist(u, v)\) 是 \(u\) 到 \(v\) 的最短路径长度(可以用 Floyd-Warshall 等算法预处理全源最短路径)。
3. 算法步骤
- 预处理全源最短路径 \(dist[u][v]\)。
- 初始化:对每个终端点 \(t \in S\),令 \(dp[t][\{t\}] = 0\)(单个终端点,无需边)。其他状态初始化为无穷大。
- 对每个 \(T \subseteq S\)(按大小从小到大枚举):
- 对每个 \(u \in V\):
- 枚举 \(T\) 的所有非空真子集 \(T'\),执行第一部分转移。
- 对每个 \(u \in V\):
- 用所有 \(v \in V\) 尝试第二部分转移(换根),直到 \(dp\) 值不再变化(类似最短路的松弛,可以迭代多次,但一般一次对所有 \(u\) 更新即可)。
- 对每个 \(u \in V\):
- 最后答案 = \(\min_{u \in V} dp[u][S]\)。
4. 复杂度
- 状态数:\(O(|V| \cdot 2^{|S|})\)。
- 第一部分转移枚举子集:对每个状态 \(O(2^{|T|})\)。
- 总复杂度大约 \(O(|V|^3 + |V| \cdot 3^{|S|} + |V|^2 \cdot 2^{|S|})\)(实际实现常用子集枚举优化到 \(O(|V|^2 \cdot 2^{|S|} + |V|^3)\))。
- 因此该算法在 \(|S|\) 较小时(比如 \(|S| \le 20\))可用,是指数级,但对大图仍受限。
举例说明
考虑一个简单图:
- 顶点:\(1, 2, 3, 4\)。
- 边:(1,2) 权重 2,(2,3) 权重 3,(3,4) 权重 1,(1,4) 权重 5,(1,3) 权重 4。
- 终端点 \(S = \{1, 3, 4\}\)。
用 Dreyfus-Wagner 算法:
- 用 Floyd-Warshall 求出全源最短路径:
- 例如 \(dist(1,3) = 4\)(直接),但经过 2 是 2+3=5,所以最短是 4。
- \(dist(1,4)\):路径 1-3-4 权重 4+1=5,1-4 直接 5,最短 5。
- 初始化:
- \(dp[1][\{1\}] = 0\),\(dp[3][\{3\}] = 0\),\(dp[4][\{4\}] = 0\)。
- 枚举终端集合大小:
- 大小 2 的集合,例如 \(T = \{1,3\}\):
- 对每个 \(u\):
- 第一部分:枚举子集 \(T' = \{1\}\) 和 \(\{3\}\):
- \(dp[1][\{1,3\}] = \min(dp[1][\{1\}] + dp[1][\{3\}])\) 初始无穷大,跳过。
- 用第二部分:\(dp[1][\{1,3\}] = \min_v (dp[v][\{1,3\}] + dist(1,v))\) 初始无穷大,跳过。
实际计算时,先对 \(T = \{1,3\}\) 尝试用最短路径连接 1 和 3:比如在 \(u=1\) 时,可以接上 \(v=3\) 的 \(dp[3][\{1,3\}]\) 加上 \(dist(1,3)=4\),但 \(dp[3][\{1,3\}]\) 还不知道,所以需要按大小计算。
- 第一部分:枚举子集 \(T' = \{1\}\) 和 \(\{3\}\):
- 对每个 \(u\):
- 更实际计算时,会先对每个终端对用最短路径初始化相应 dp 状态。
- 大小 2 的集合,例如 \(T = \{1,3\}\):
- 最终得到最小斯坦纳树:连接 1-3(权重 4)和 3-4(权重 1),总权重 5。也可以 1-4(权重 5)和 3-4(权重 1)总权重 6,不是最小。所以最优是 5。
总结
- 斯坦纳树问题是经典 NP-hard 问题,Dreyfus-Wagner 算法用动态规划在终端点很少时能求精确解。
- 核心思想:以每个顶点为“根”,枚举终端子集,合并子树或通过最短路径换根。
- 实际应用中若 \(|S|\) 很小(≤ 20),此算法可行;若 \(|S|\) 大,则需用启发式或近似算法(如最小生成树近似比 2 )。