AcWing 529. 宝藏
原题链接
困难
作者:
炽热的
,
2022-05-17 11:13:55
,
所有人可见
,
阅读 158
具体看 yxc 和 彩铅大佬 的题解,灰常不错!
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 12, M = 1 << 12, INF = 0x3f3f3f3f;
int n, m;
int d[N][N], g[M];
int f[M][N];
int main()
{
scanf("%d%d", &n, &m);
memset(d, 0x3f, sizeof d);
for (int i = 0; i < n; i ++ ) d[i][i] = 0;
while (m -- )
{
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
a -- , b -- ;
d[a][b] = d[b][a] = min(d[a][b], w);
}
// 处理每个联通块(集) 能扩展到的边
for (int i = 1; i < 1 << n; i ++ )
for (int j = 0; j < n; j ++ )
if (i >> j & 1)
for (int k = 0; k < n; k ++ )
if (d[j][k] != INF) g[i] |= 1 << k;
memset(f, 0x3f, sizeof f);
// 初始化起点花费
for (int i = 0; i < n; i ++ ) f[1 << i][0] = 0;
for (int i = 1; i < 1 << n; i ++ ) // 枚举全集
for (int j = (i - 1) & i; j; j = (j - 1) & i) // 枚举全集的 非全集子集
if ((g[j] & i) == i) // i 能够通过 子集 j 延伸而来
{
int remain = i ^ j; // 从子集到全集还需要扩展的节点
int cost = 0;
for (int k = 0; k < n; k ++ )
if (remain >> k & 1)
{
int t = INF;
for (int u = 0; u < n; u ++ )
if (j >> u & 1)
t = min(t, d[k][u]); // 选择一条最短的路径
cost += t;
}
for (int k = 1; k < n; k ++ ) f[i][k] = min(f[i][k], f[j][k - 1] + cost * k);
}
int res = INF;
for (int i = 0; i < n; i ++ ) res = min(res, f[(1 << n) - 1][i]);
printf("%d\n", res);
return 0;
}