Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

斯坦纳树模板

求最小斯坦纳树。

问题要求一个子图,实际上答案一定是一棵树。

注意到 $k$ 比较小,考虑状压,$f _ {i, s}$ 表示当前树的根节点点为 $i$ ,已经连通了集合 $s$ 中的点,每次只从根上考虑。对于同一个根的两个集合,可以将其合并,即 $f _{i, s} = \min _ {t \subset s} f _ {i, t} + f _ {i, s - t}$ ;也可以任意添加一条与根相连的边,并改变根,即 $f _ {i, s} = \min _ {j} f _ {j, s} + w _ {i, j}$ 。考虑对于每一种状态,第一种转移可以枚举子集,第二种转移本质就是最短路。那么总复杂度为 $O(3 ^ k + 2 ^ k nm)$ 。

查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <cstdio>
#include <queue>
using namespace std;
template <class Type>
void read (Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 1) + (x << 3) + c - '0';
flag && (x = ~x + 1);
}
template <class Type, class ...rest>
void read (Type &x, rest &...y) { read(x), read(y...); }
template <class Type>
void write (Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar('0' + x % 10);
}
const int N = 110, M = 1e3 + 10, K = 10, inf = 1e9;
bool vis[N];
int n, m, k, p[K], f[N][1 << K];
int idx, hd[N], nxt[M], edg[M], wt[M];
void add (int a, int b, int c)
{
nxt[++idx] = hd[a];
hd[a] = idx;
edg[idx] = b;
wt[idx] = c;
}
int main ()
{
read(n, m, k);
for (int i = 1; i <= n; ++i) hd[i] = -1;
for (int a, b, c; m; --m)
{
read(a, b, c);
add(a, b, c), add(b, a, c);
}
for (int i = 1; i <= n; ++i)
for (int j = 0; j < 1 << k; ++j) f[i][j] = inf;
for (int i = 0; i < k; ++i)
read(p[i]), f[p[i]][1 << i] = 0;
for (int s = 1; s < 1 << k; ++s)
{
queue <int> q;
for (int i = 1; i <= n; ++i)
{
for (int t = s; t; --t &= s)
f[i][s] = min(f[i][s], f[i][t] + f[i][s ^ t]);
if (f[i][s] ^ inf) q.push(i);
}
while (!q.empty())
{
int t = q.front(); q.pop();
vis[t] = false;
for (int i = hd[t]; ~i; i = nxt[i])
if (f[t][s] + wt[i] < f[edg[i]][s])
{
f[edg[i]][s] = f[t][s] + wt[i];
if (!vis[edg[i]])
{
q.push(edg[i]);
vis[edg[i]] = true;
}
}
}
}
write(f[p[0]][(1 << k) - 1]);
return 0;
}