最小斯坦纳树

老是忘掉这个东西,稍微记一下。

不难观察得到最终答案是一棵树。

涉及到树形的 dp 和状压,考虑状态 dp(u,S) 表示 u 为树根,目前连接起来的点状态为 S 的最小代价。

转移分两种:

  • 树根 uvdp(v,S)dp(u,S)+dis(u,v)
  • 两棵子树合并:dp(u,S)dp(u,T)+dp(u,ST)

考虑按大小顺序枚举 S,第一种相当于最短路,第二种枚举子集,时间复杂度 O(nm2k+n3k)。用了 SPFA 求最短路。

#include <bits/stdc++.h>
#define pb emplace_back
#define fir first
#define sec second
using i64 = long long;
using u64 = unsigned long long;
using pii = std::pair<int,int>;
const i64 mod = 998244353;
const int maxn = 505;
const int maxm = 1 << 10;
int f[maxm][maxn], n, m, k;
bool inq[maxn];
std::vector<pii> G[maxn];
std::queue<int> q;
int main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
scanf("%d %d %d", &n, &m, &k);
for(int i = 1;i <= m;++ i) {
int u, v, t;
scanf("%d %d %d", &u, &v, &t);
G[u].pb(v, t);
G[v].pb(u, t);
}
memset(f, 0x3f, sizeof(f));
for(int i = 1;i <= k;++ i) {
int x;
scanf("%d", &x);
f[1 << (i - 1)][x] = 0;
}
for(int S = 1;S < (1 << k);++ S) {
for(int T = (S - 1) & S;T;T = (T - 1) & S) {
for(int i = 1;i <= n;++ i)
f[S][i] = std::min(f[S][i], f[T][i] + f[S ^ T][i]);
}
for(int i = 1;i <= n;++ i)
q.emplace(i), inq[i] = true;
int mn = 0x3f3f3f3f, pos = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
inq[u] = false;
for(auto& [v, w] : G[u])
if(f[S][v] > f[S][u] + w) {
f[S][v] = f[S][u] + w;
if(!inq[v])
inq[v] = true, q.emplace(v);
}
}
}
int ans = 0x3f3f3f3f;
for(int i = 1;i <= n;++ i)
ans = std::min(f[(1 << k) - 1][i], ans);
printf("%d\n", ans);
return 0;
}
posted @   ImALAS  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
历史上的今天:
2022-07-14 AtCoder Beginner Contest 259 题解
点击右上角即可分享
微信分享提示