牛客-996D 最短Hamilton路径(状态压缩DP)¶
链接:https://ac.nowcoder.com/acm/contest/996/D
题目描述¶
给定一张 n(n \leq 20) 个点的带权无向图,点从0 \sim 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路径的长度。
示例1¶
输入¶
4
0 2 1 3
2 0 2 1
1 2 0 1
3 1 1 0
输出¶
4
分析:用d[s][i]
表示已经访问过的点的状态,目前刚好处于点i
,最后到达n - 1
的最短路径。状态转移方程:刚好处于点i
的意思就是在上一个状态点i
还没有被访问,假设处于点j
,在上一时刻点j
已经被访问了,恰好从点j
转移到点i
。
为了方便的表示一个点是否被访问,选择用位运算来表示集合状态。
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f;
//vector<vector<int> > dis(20, vector<int>(20)), d(1 << 20, vector<int>(20, INF));
int n;
int dis[20][20], d[1 << 20][20];
int solve()
{
memset(d, 0x3f, sizeof(d));
d[1][0] = 0;
for (int s = 1; s < 1 << n; ++s) {
for (int i = 0; i < n; ++i) {
if (s >> i & 1) { //点i刚好被访问
for (int j = 0; j < n; ++j) {
//点j已经被访问
if ((s ^ 1 << i) >> j & 1) {
d[s][i] = min(d[s][i], d[s ^ 1 << i][j] + dis[i][j]);
}
}
}
}
}
return d[(1 << n) - 1][n - 1];
}
int main()
{
std::ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> n;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cin >> dis[i][j];
}
}
cout << solve() << endl;
return 0;
}