luogu P6192 【模板】最小斯坦纳树
https://www.luogu.com.cn/problem/P6192
分两种情况转移
设 d p [ u ] [ S ] dp[u][S] dp[u][S]表示以 u u u为根,包含关键点的集合为 S S S的最小生成树边权和
显然可以枚举子集转移
d p [ u ] [ S ] = d p [ u ] [ S 0 ] + d p [ u ] [ S − S 0 ] dp[u][S]=dp[u][S0]+dp[u][S-S0] dp[u][S]=dp[u][S0]+dp[u][S−S0]
或者考虑从其它点转移过来
d
p
[
u
]
[
S
]
=
d
p
[
v
]
[
S
]
+
w
(
v
,
u
)
dp[u][S]=dp[v][S]+w(v,u)
dp[u][S]=dp[v][S]+w(v,u)
跑个dijkstra即可
代码实现很简单
code:
#include<bits/stdc++.h>
#define N 105
#define M 505
using namespace std;
struct edge {
int v, c, nxt;
} e[M << 1];
int p[N], eid;
void init() {
memset(p, -1, sizeof p);
eid = 0;
}
void insert(int u, int v, int c) {
e[eid].v = v;
e[eid].c = c;
e[eid].nxt = p[u];
p[u] = eid ++;
}
priority_queue<pair<int, int> > q;
int vis[N], f[N][(1 << 10) + 5];
void dij(int S) {
memset(vis, 0, sizeof vis);
while(q.size()) {
int u = q.top().second; q.pop();
vis[u] = 1;
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v, c = e[i].c;
if(f[v][S] > f[u][S] + c) {
f[v][S] = f[u][S] + c;
q.push(make_pair(- f[v][S], v));
}
}
}
}
int n, m, k;
int main() {
init();
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= m; i ++) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
insert(u, v, c), insert(v, u, c);
}
memset(f, 0x3f, sizeof f);
int inf = f[0][0];
int rt = 0;
for(int i = 1; i <= k; i ++) {
int x;
scanf("%d", &x); rt = x;
f[x][1 << (i - 1)] = 0;
}
for(int S = 0; S < (1 << k); S ++) {
for(int i = 1; i <= n; i ++) {
for(int S0 = S; S0; S0 = (S0 - 1) & S) {
f[i][S] = min(f[i][S], f[i][S0] + f[i][S ^ S0]);
}
if(f[i][S] != inf) q.push(make_pair(- f[i][S], i));
}
dij(S);
}
printf("%d", f[rt][(1 << k) - 1]);
return 0;
}