这道题真的给我留下了创伤……废了4天七八个小时才AC……
先发一张耀眼的AC图:
啊心情好多了。
另外听说某巨佬爆搜跑了0ms,n在20以内都秒过……先%为敬,tql。
好了我们先看看数据范围,
n
≤
12
n\leq 12
n≤12,状压DP无疑了……
看了数据范围再看题面,这道题类似最小生成树。kruskal肯定不行,prim也会被卡掉。
最后连出的边一定形成一棵树,在计算过程中我们要在状态中保存当前树的高度计算题目中的 L L L 值。
所以状态:设 f [ i ] [ S ] f[i][S] f[i][S] 表示树高为 i + 1 i+1 i+1(这是个人的习惯,也可以设树高为 i i i ),当前联通的点为集合 S S S 中的最小代价。
方程为: f [ i ] [ S ] = f [ i − 1 ] [ S 0 ] + p a y × i ( S 0 ∈ S ) f[i][S]=f[i-1][S0]+pay\times i(S0\in S) f[i][S]=f[i−1][S0]+pay×i(S0∈S),其中 p a y pay pay 为这次连边的总长度。
接下来的重点就落在了 p a y pay pay 的计算上。我们可以预处理一个数组, g a p [ i ] [ j ] gap[i][j] gap[i][j] 表示集合 j j j 的所有元素像集合 i i i 中的任意元素连一条边的最小花费。
再预处理一个数组, d i s [ i ] [ S ] dis[i][S] dis[i][S] 表示点 i i i 向集合 S S S 中任意一个点连一条边的最小花费。
d i s dis dis 数组灰常容易计算,有了 d i s , g a p dis,gap dis,gap 也就是唾手可得的东西了。有了 g a p gap gap, f f f 数组的计算就很爽歪歪了:只需将方程修改为 f [ i ] [ S ] = f [ i − 1 ] [ S 0 ] + g a p [ S 0 ] [ S f[i][S]=f[i-1][S0]+gap[S0][S f[i][S]=f[i−1][S0]+gap[S0][S ^ S 0 ] × i ( S 0 ∈ S ) \hat{} S0]\times i(S0\in S) ^S0]×i(S0∈S) 即可。
C o d e : Code: Code:
#include <cstdio>
#include <cstring>
#define min(x, y) (x < y ? x : y)
int mp[13][13], dis[13][1 << 12], gap[1 << 12][1 << 12], f[13][1 << 12], len[1 << 12], n;
inline bool scan() {
memset(mp, 0x3f, sizeof(mp));
int m, u, v, w;
scanf("%d%d", &n, &m);
if (m == 0) {
putchar('0');
return false;
}
for (register int i(1); i < 1 << n; ++ i) {
register int k(i);
while (k) {if (k & 1) ++ len[i]; k >>= 1;}
}
while (m --) {
scanf("%d%d%d", &u, &v, &w);
-- u, -- v;
mp[u][v] = mp[v][u] = min(mp[u][v], w);
}
return true;
}
inline void init() {
memset(dis, 0x3f, sizeof(dis));
for (register int i(0); i < n; ++ i)
for (register int s(1); s < 1 << n; ++ s) if (!(s & 1 << i)) {
for (register int k(0); k < n; ++ k)
if (s & 1 << k) dis[i][s] = min(dis[i][s], mp[i][k]);
}
for (register int i(1); i < 1 << n; ++ i) {
register int s0((1 << n) - 1 ^ i);
for (register int j(1); j < 1 << n; ++ j) if (!(i & j)){
for (register int k(0); k < n; ++ k)
if (j & 1 << k)
if (dis[k][i] <= 1e9) gap[i][j] += dis[k][i];
else {gap[i][j] = -1; break;}}
else gap[i][j] = -1;
}
}
inline int DP() {
int ans(0x7ffffff);
memset(f, 0x3f, sizeof(f));
for (register int i(0); i < n; ++ i) f[0][1 << i] = 0;
for (register int i(1); i < n; ++ i)
for (register int S(1); S < 1 << n; ++ S) if (len[S] > i) {
for (register int S0(S - 1 & S); S0; S0 = S0 - 1 & S)
if (gap[S0][S ^ S0] != -1 && f[i - 1][S0] <= 1e9)
f[i][S] = min(f[i][S], f[i - 1][S0] + i * gap[S0][S ^ S0]);
}
for (register int i(0); i < n; ++ i) ans = min(ans, f[i][(1 << n) - 1]);
return ans;
}
int main() {
if (!scan()) return 0;//不特判一下你就知道
init();
printf("%d", DP());
}