Hie with the Pie
题目链接:POJ 3311
题目大意
给你 n+1 个点,其中 0 号点是特殊点。
然后两个点之间都有路径可以走。
然后你要从特殊点出发走过所有点回到特殊点,问你最短路径。
思路
不难看出是状压 DP。
直接设
f
i
,
j
f_{i,j}
fi,j 为每个点的走过状态为
i
i
i,最后走的点是
j
j
j。
然后直接枚举下一个点转移即可。
然后答案就是枚举
i
i
i,是
f
2
n
−
1
,
i
+
a
i
,
0
f_{2^n-1,i}+a_{i,0}
f2n−1,i+ai,0 的最小值。
(因为你还要走回来)
然后你直接用给你的
a
a
a 做会错。
因为你不一定要走图上给你的边,你走
1
→
3
1\rightarrow 3
1→3 可能可以
1
→
2
,
2
→
3
1\rightarrow 2,2\rightarrow 3
1→2,2→3,这样可能反而更优。(就比如第一个边权是
10
10
10,后面两个边权是
1
1
1)
所以我们先对
a
a
a 跑 Floyed 算法求出两两点最短路,就可以做了。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
int n, a[11][11];
ll f[1024][11];
int main() {
scanf("%d", &n);
while (n) {
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++)
scanf("%d", &a[i][j]);
memset(f, 0x7f, sizeof(f));
for (int k = 0; k <= n; k++)//Floyed
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++)
a[i][j] = min(a[i][j], a[i][k] + a[k][j]);
for (int i = 1; i <= n; i++)
f[1 << (i - 1)][i] = a[0][i];
for (int i = 0; i < (1 << n); i++) {//原来状态
for (int j = 1; j <= n; j++)//最后一个到的点是哪个
if ((i >> (j - 1)) & 1) {
for (int k = 1; k <= n; k++)//现在要去的点是哪
if (!((i >> (k - 1)) & 1)) {
f[i | (1 << (k - 1))][k] = min(f[i | (1 << (k - 1))][k], f[i][j] + a[j][k]);
}
}
}
ll ans = INF;
for (int i = 1; i <= n; i++) ans = min(ans, f[(1 << n) - 1][i] + a[i][0]);//枚举最后到的点加上回来的时间
printf("%lld\n", ans);
scanf("%d", &n);
}
return 0;
}