最短Hamilton路径
给定一张 nn 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。
输入格式
第一行输入整数n。
接下来n行每行n个整数,其中第i行第j个整数表示点ii到jj的距离(记为a[i,j])。
对于任意的x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]>=a[x,z]。
输出格式
输出一个整数,表示最短Hamilton路径的长度。
数据范围
1≤n≤20
0≤a[i,j]≤10^7
输入样例:
5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0
输出样例:
18
链接:
https://www.acwing.com/problem/content/93/
思路:
本题用到了二进制状态压缩DP,复杂度为O(n2*2n)
用二进制来标记哪个点走过,1代表走过,0代表未走。我们还需知道当前处于哪个点,用j表示。那么我们可以定义dp[i][j],i代表当前哪些点走过,j表示当前处于哪个点。
那么起点应该为dp[1][0] = 0 (i 只有第0位为1),最终状态为dp[(1<<n)-1][n-1](所有点都经过,且当前点为n-1)。
在任意时刻,我们可以推出dp[i][j] = min(dp[i][j], dp[i xor (1<<j)][k] + a[k][j])。 i xor (1 << j)表示将i的第j位取反,也就是上一时刻j是未经过的,上一时刻变为当前时刻需要一个中间变量k来完成,这时的k必须是走过的。所以状态转移方程就变为了dp[i][j] = min(dp[i][j], dp[i xor (1<<j)][k] + a[k][j])。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 21;
const int INF = 0x3f3f3f3f;
int a[maxn][maxn];
int dp[1<<maxn][maxn];
int main()
{
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
scanf("%d", &a[i][j]);
memset(dp, INF, sizeof(dp));
dp[1][0] = 0;
for(int i = 1; i < (1<<n); i++) {
for(int j = 0; j < n; j++) {
if((i >> j) & 1) { //判断i的第j位是不是1
for(int k = 0; k < n; k++) {
if((i >> k) & 1) { 判断i的第k位是不是1
dp[i][j] = min(dp[i][j], dp[i^(1<<j)][k] + a[k][j]); //i^(1<<j)将i的第j位取反
}
}
}
}
}
printf("%d\n", dp[(1<<n)-1][n-1]);
return 0;
}