题目: 91. 最短Hamilton路径 - AcWing题库https://www.acwing.com/problem/content/description/93/
思路分析:
用f[i, j] 表示 我们目前走过的所有点的路径, 以下思路来自acwing y总讲解, 整理成文字:
状态表示:
-
集合: 所有从0开始走到j, 走过的所有点存在i当中的所有路径
-
i是一个二进制数 每一位分别表示当前点是不是走过了. i是一个压缩的状态
-
比如: i=(1110011) 1代表该点走过了 0代表没走过
-
-
属性: min
状态计算:
-
由于最后一个点是确定已知的, 所以以倒数第二个点是哪个点来分类
-
f[i,j]的分类方法: 以倒数第二个点是0/1/2/...n-1 点分类, 倒数第二个点设为k
-
距离是 f [i 除去j点的状态, k] + a(k,j)
代码:
代码来自y总和acwing的两个题解, 两位都写的很详细:AcWing 91. 最短Hamilton路径(超详解) - AcWinghttps://www.acwing.com/solution/content/18533/
AcWing 91. 最短Hamilton路径 - AcWinghttps://www.acwing.com/solution/content/789/
我通过他们俩的内容进行了一些注释的修改, 方便自己理解, 整理成下面的内容:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 20, M = 1 << N;
int n;//点的数量
int w[N][N];//w数组代表两点之间的距离 也就是无向图
int f[M][N];//f数组存的是dp的状态 一维存储的是状态/情况数 二维存储的是距离
int main() {
cin >> n;//输入点的数量
//遍历 输入点之间的距离
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
cin >> w[i][j];
//初始化数据
memset(f, 0x3f, sizeof f);//因为题目需要求最小值 初始化为一个无穷大值0x3f
f[1][0] = 0;//0点是起点,从0走到0, 状态是1距离是0, 不需要任何做功 就初始化成0
for (int i = 0; i < 1 << n; i++)//i表示所有的情况/状态的集合, 每一个二进制位是1或0代表这个点是否经过
for (int j = 0; j < n; j++)//j表示当前枚举哪一个点
if (i >> j & 1)//如果i状态的第j位为1,也就是到达过j点, 即点j在状态i里经过了
for (int k = 0; k < n; k++)//枚举j的前一个点k, k表示走到j这个点之前,以k为终点的最短距离
if (i >> k & 1)//如果j状态的第k位是1, 也就是j路线到达过k点
f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j]);//状态转移 更新最短距离
//f[i][j]是之前的最短距离
// f[i - (1 << j)][k]是以倒数第二个点为终点的最短距离, w[k][j]是最后一段的最短距离
cout << f[(1 << n) - 1][n - 1] << endl;//输出最后的最优值 表示所有点都走过了,且终点是n-1的最短距离
//位运算的优先级低于'+'-'所以有必要的情况下要打括号
return 0;
}