Loading

斯坦纳树

给一张图,其中一些点称为关键点。

斯坦纳树的定义就是能覆盖关键点的连通子图,为什么叫连通子图,可以发现答案子图可以为一棵树,否则如果有环,可以删去环上任意一条边,新图也联通。

\(\textup{最小斯坦纳树}\)

\(Link\)

题意:给定一张带权无向图,其中有一些点是关键点,求连通子图的最小长度。

可以发现答案子图一定为一棵树,否则如果有环,可以删去环上任意一条边,新图也联通。

这是个 NP-hard 问题,可能没办法在多项式时间复杂度内(\(\mathcal{O}(\mathcal{n}^k)\)\(\mathcal{O}(\mathcal{nk})\))解决,考虑状压。

子图的状态肯定无法压进 dp,考虑将关键点的状态压进 dp。

由于答案是一棵树,可以设 \(f_{i,S}\) 表示以 \(i\) 为根,且关键点覆盖状态为 \(S\) 的最小长度。

我们可以将以 \(i\) 为根的树分成两类:\(i\) 的度数为 \(1\)\(i\) 的度数大于 \(1\)

分讨:

  • \(i\) 的度数为 \(1\),则这个点的转移来自于他唯一的连边,我们就考虑通过 \(i\)的相邻点来转移,设这个点为 \(j\),则

\[f_{i, S} = f_{j, S} + dis_{i,j} \]

  • \(i\) 的度数大于 \(1\),则可以将这个图分成 \(i\) 的若干个子树,就可以通过枚举子集转移:

\[f_{i,S} = \min\{f_{i, T} + f_{i, S - T} \} \]

实现方面,对于第一种转移,可以想到最短路算法的松弛操作,将整张图松弛,先将整张图更新的点扔进队列,然后用第一种转移的转移式更新整张图的 \(f\),通过最短路算法实现。

可以发现图是比较稀疏的,可以复活死去的 \(SPFA\), 这个转移的时间复杂度卡满的话是 \(\mathcal{O}(2^knm)\),和枚举子集比起来你会惊奇发现这和枚举子集的时间复杂度差不多。

对于第二种转移,直接枚举子集,复杂度 \(\mathcal{O}(3^kn)\)

讲一下枚举子集的时间复杂度,之前一直不是很懂。

设集合大小为 \(T\),则大小为 \(0\) 的子集个数为 \(C_n^0\),大小为 \(1\) 的子集个数为 \(C_n^1\)...大小为 \(n\) 的子集个数为 \(C_n^n\)

枚举的总复杂度就是 \(2^0 \times C_n^0 + 2^1 + C_n^1 + ... 2^n + c_n^n = \sum_{k = 0}^{n} 2^k C_n^k = \sum_{k = 0}^{n} 2^k 1^{n-k}C_n^k = (2 + 1)^n = 3^n\)

最后 \(ans = \min \{f_{i,2^{k} - 1}\}\)

游览计划

可以发现这题很板,只是加了一个点权转边权和输出方案。

输出方案在每次转移时加一个 \(pre\) 数组,然后 dfs。

点权转边权可以考虑将第二个转移改成

\[f_{i,S} = \min\{f_{i, T} + f_{i, S - T} - val_i\} \]

因为两边我们都算了一次 \(i\) 节点,减了就行。

posted @ 2024-10-05 21:37  faith_xy  阅读(62)  评论(1)    收藏  举报