题面
解法
为什么我的状压dp那么丑啊……
- 发现 n ≤ 12 n≤12 n≤12,所以不妨考虑状压dp
- 设 f [ d ] [ S ] [ r t ] f[d][S][rt] f[d][S][rt]表示当前深度为 d d d,在根为 r t rt rt的子树中有 S S S集合内的点的最小代价
- 考虑先枚举 r t rt rt的一个儿子是什么,假设为 x x x,然后枚举集合 S ′ ⊂ S S'\subset S S′⊂S作为 x x x的子树部分,其它剩下的部分继续作为除 x x x以外的子树部分
- 可以发现这个dp很好转移: f [ d ] [ S ] [ r t ] = m i n ( d × v [ r t ] [ x ] + f [ d + 1 ] [ S ′ ] [ x ] + f [ d ] [ S − S ′ ] [ r t ] ) f[d][S][rt]=min(d×v[rt][x]+f[d+1][S'][x]+f[d][S-S'][rt]) f[d][S][rt]=min(d×v[rt][x]+f[d+1][S′][x]+f[d][S−S′][rt])
- 时间复杂度: O ( 3 n n 3 ) O(3^nn^3) O(3nn3)
- 为什么是这个复杂度呢?因为枚举子集的整体复杂度为 O ( 3 n ) O(3^n) O(3n),这个可以使用二项式定理来证明。关于 n n n的状态为 O ( n 2 ) O(n^2) O(n2),转移的复杂度为 O ( n ) O(n) O(n),所以总复杂度为 O ( 3 n n 3 ) O(3^nn^3) O(3nn3)
代码
#include <bits/stdc++.h>
#define inf 1ll << 40
#define ll long long
#define N 13
using namespace std;
ll lg[1 << N], a[N][N], f[N][1 << N][N];
int lowbit(int x) {return x & -x;}
ll dp(int d, int S, int rt) {
if (!S) return 0;
if (f[d][S][rt]) return f[d][S][rt];
ll ret = inf;
for (int tx = S, ty = lowbit(S); tx; tx -= ty, ty = lowbit(tx)) {
int x = lg[ty], y = S ^ ty; ll tmp = a[rt][x] * d;
for (int s = y; s; s = (s - 1) & y)
ret = min(ret, tmp + dp(d + 1, s, x) + dp(d, y ^ s, rt));
ret = min(ret, tmp + dp(d, y, rt));
}
return f[d][S][rt] = ret;
}
int main() {
ios::sync_with_stdio(false);
int n, m; cin >> n >> m;
for (int i = 2; i < (1 << n); i++) lg[i] = lg[i >> 1] + 1;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
a[i][j] = inf;
for (int i = 1; i <= m; i++) {
int x, y; ll v; cin >> x >> y >> v;
x--, y--; ll tx = min(a[x][y], v);
a[x][y] = a[y][x] = tx;
}
ll ans = inf, mask = (1 << n) - 1;
for (int i = 0; i < n; i++)
ans = min(ans, dp(1, mask ^ (1 << i), i));
cout << ans << "\n";
return 0;
}