描述
给定一张 n(n≤20) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。
输入格式
第一行一个整数n。
接下来n行每行n个整数,其中第i行第j个整数表示点i到j的距离(一个不超过10^7的正整数,记为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路径的长度。
样例输入
4
0 2 1 3
2 0 2 1
1 2 0 1
3 1 1 0
样例输出
4
样例解释
从0到3的Hamilton路径有两条,0-1-2-3和0-2-1-3。前者的长度为2+2+1=5,后者的长度为1+2+1=4
状态压缩DP
使用二进制数来表示点是否被经过,如果i
位为1表示被经过,否则未被经过
使用
d
p
[
i
]
[
j
]
(
0
≤
i
<
2
n
,
0
≤
j
<
n
)
dp[i][j] (0 \le i < 2^n,0\le j < n)
dp[i][j](0≤i<2n,0≤j<n)来表示点被经过的状态为i
处于位置j
时的最短路径
起始状态dp[1][0]
,只有0点被经过,最少步数为0,目标状态dp[(1<<n)-1][n-1]
,所有的点都被经过,并处于终点
方法:
通过枚举所有的可能状态来寻找最短路径,类似于CH0201费解的开关中枚举第一行的所有状态,前提:每个点只能被经过一次
如果当前状态i
下经过了点j
,因为每个点只能被经过一次,所以j
一定是被刚刚经过,而上一时刻所在的点k
可能是任意位置,通过枚举所有可能的点k
并考虑从点k
到j
距离的最小值,就能把转移方程确定下来了
枚举所有状态是
2
n
2^n
2n,枚举点j
是
n
n
n,枚举点k
也是
n
n
n,所以时间复杂度为
O
(
2
n
∗
n
2
)
O(2^n*n^2)
O(2n∗n2)
代码实现:
枚举所有的状态 i ∈ [ 1 , 1 < < n ) i\in [1,1<<n) i∈[1,1<<n) ,寻找当前已经经过了的点 j ∈ [ 0 , n − 1 ] j\in[0,n-1] j∈[0,n−1] ,寻找 在没有经过点 j j j的情况下 是从哪个点 k ∈ [ 0 , n − 1 ] k \in [0,n-1] k∈[0,n−1]来到 j j j的,取路径的最小值
经过点j
表示为状态i
第j
位为1:i>>j&1
(取i
中第j
位)
当前状态其他点状态不变,不经过点j
表示为:i'=i^1<<j
(把i
的j
位取反)
寻找k
点i'>>k&1
同(i^1<<j)>>k&1
状态转移方程
dp[i][j]=min(dp[i][j],dp[(i^1<<j)][k]+value[k][j]);
dp
初值赋为无穷
经过了j
点并处在j
点的最短路径为,从没有经过j
的状态下的k
点到达j
,并加上从k
到j
的权值
ps:位运算优先级
加减 | 移位 | 比较大小 | 位与 | 异或 | 位或 |
---|---|---|---|---|---|
+,- | <<,>> | >,<,==,!= | & | xor(^) | | |
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
static const auto io_sync_off = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
return nullptr;
}();
const int maxn = 22;
int v[maxn][maxn];
int dp[1 << 20][maxn];
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
cin >> v[i][j];
memset(dp, 0x3f, sizeof(dp));
dp[1][0] = 0;
for (int i = 1; i < 1 << n; ++i)//状态
for (int j = 0; j < n; ++j)//j点
if (i >> j & 1)
for (int k = 0; k < n; ++k)//k点
if ((i ^ 1 << j) >> k & 1)
dp[i][j] = min(dp[i][j], dp[i ^ 1 << j][k] + v[k][j]);
cout << dp[(1 << n) - 1][n - 1];
return 0;
}