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][SS0]

或者考虑从其它点转移过来
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;
}
posted @ 2021-10-13 15:31  lahlah  阅读(45)  评论(0编辑  收藏  举报