LOJ 2318 「NOIP2017」寶藏
阿新 • • 發佈:2018-12-12
題面
解法
為什麼我的狀壓dp那麼醜啊……
- 發現,所以不妨考慮狀壓dp
- 設表示當前深度為,在根為的子樹中有集合內的點的最小代價
- 考慮先列舉的一個兒子是什麼,假設為,然後列舉集合作為的子樹部分,其它剩下的部分繼續作為除以外的子樹部分
- 可以發現這個dp很好轉移:
- 時間複雜度:
- 為什麼是這個複雜度呢?因為列舉子集的整體複雜度為,這個可以使用二項式定理來證明。關於的狀態為,轉移的複雜度為,所以總複雜度為
程式碼
#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;
}