最小斯坦纳树

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

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

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

转移分两种:

  • 树根 \(u\to v\)\(dp(v,S)\gets dp(u,S) + dis(u,v)\)
  • 两棵子树合并:\(dp(u,S)\gets dp(u,T)+dp(u,S-T)\)

考虑按大小顺序枚举 \(S\),第一种相当于最短路,第二种枚举子集,时间复杂度 \(\mathcal O(nm2^k+n3^k)\)。用了 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 @ 2023-07-14 08:29  ImALAS  阅读(10)  评论(0编辑  收藏  举报