[UOJ 333][NOIP2017]宝藏
转载请注明出处:http://danihao123.is-programmer.com/
啊啊啊爽到力,,,
过去太池沼力,没有做出来惹。现在看了看可能比较简单罢……
定义状态\(d_{i, S}\)表示当前已经被覆盖的点集为\(S\),然后当前的生成树已经考虑到了第\(i\)层,转移看起来很毒……
所以预处理一个点集到另一个点集只走一步边来扩展,最优解是多少,然后转移就方便力……
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
const int maxn = 12;
int G[15][15];
const int INF = 0x3f3f3f3f;
void add_edge(int u, int v, int d) {
G[u][v] = std::min(G[u][v], d);
G[v][u] = std::min(G[v][u], d);
}
int n;
int g1[(1 << maxn)][15], g2[(1 << maxn)][(1 << maxn)];
void process() {
for(int s = 1; s < (1 << n); s ++) {
for(int i = 1; i <= n; i ++) {
g1[s][i] = INF;
for(int j = 0; j < n; j ++) {
if((1 << j) & s) {
g1[s][i] = std::min(g1[s][i], G[j + 1][i]);
}
}
}
}
for(int s = 1; s < (1 << n); s ++) {
int k = (1 << n) - 1; k = k & (~s);
for(int t = k; t > 0; t = (t - 1) & k) {
g2[s][t] = 0;
for(int i = 1; i <= n; i ++) {
if((1 << (i - 1)) & t) {
if(g1[s][i] == INF) {
g2[s][t] = INF; break;
}
g2[s][t] += g1[s][i];
}
}
}
}
}
using ll = long long;
ll d[14][(1 << maxn)];
ll dp() {
for(int s = 0; s < (1 << n); s ++) {
d[1][s] = INF;
}
for(int i = 0; i < n; i ++) {
d[1][(1 << i)] = 0;
}
for(int i = 2; i <= n; i ++) {
for(int s = 1; s < (1 << n); s ++) {
d[i][s] = INF;
for(int t = (s - 1) & s; t != 0; t = (t - 1) & s) {
d[i][s] = std::min(d[i][s], d[i - 1][t] + (ll(i - 1)) * g2[t][s ^ t]);
}
}
}
ll ans = INF;
for(int i = 1; i <= n; i ++) {
ans = std::min(ans, d[i][(1 << n) - 1]);
}
return ans;
}
int main() {
memset(G, 0x3f, sizeof(G));
int m; scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i ++) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
add_edge(u, v, w);
}
process(); printf("%lld\n", dp());
return 0;
}
评论 (0)