用一个整数来表示一个状态,整数把它当做二进制的数,二进制数中的每一位是0还是1,表示俩种不同的情况case 1: 0→1→2→3→4
case 2: 0→1→3→2→4
case 3: 0→2→1→3→4
case 4: 0→2→3→1→4
case 5: 0→3→1→2→4
case 6: 0→3→2→1→4
case 1 和 case 3,可以发现,我们在计算从点 0 到点 3 的路径时,其实并不关心这两中路径经过的点的顺序,而是只需要这两种路径中的较小值,因为只有较小值可能对答案有贡献。当前经过的点集,当前到了哪个点。i位为 1/0 表示是/否经过了点 i。状态表示
f[state][j]。其中 state 是一个二进制数,表示点集的方法如上述所示。
集合:经过的点集为 state,且当前到了点 j 上的所有路径。属性:路径总长度的最小值状态计算
f[state][j] = min{f[state ^ (1 << j)][k] + w[k][j]}w[k][j]表示从点 k 到点 j 的距离state ^ (1 << j)是将 state 的第 j 位改变后的值,即
由于到达点 j 的路径一定经过点 j,也就是说当 state 的第 j 位为 1 的时候,f[state][j]才可以被转移,所以 state ^ (1 << j) 其实就是将 state的第 j 位改为 0,这样也就符合了 走到点 k 的路径就不能经过点 j 这个条件。
f[state][j] = f[state_k][k] + weight[k][j] 假设是从k这个点转移过来的,加上从k到j的距离
stata_k = state出掉j之后的集合,state_k要包含k
用二进制的整数表示state,用20位整数来表示,当为1,则走过,为0没走过
(如果题目给了5个点,那么需要5位的二进制表示)当走完所有点后,5位全会变成1
而5个1的状态,必然是由4个1,1个0的状态转变过来的(只需要关心,这4个1的点,从哪一个点走到0这个点的路径最短)
只需要关心0的状态转移 4个0 -> 3个0 -> 2个0 -> 1个0 ->全1 ,维护他们之间状态转移的最小值
dp[i][j]表示从第i个状态,也就是那个点被用过(01011),停在了第j号点(j不是最终完成任务的点,题目是从0号点标记的)——>(i状态下的第j位)
dp[i][j] -> 当变成1个0时,从2个0状态变过来的最短路径(可能从1号点过来最短,可能2号,可能3号)
01011 1
01011 2
01011 3
f[state][j]的定义,要输出 f[111⋯11(n个1)][n−1]。

#include
#include
using namespace std;
const int N = 20, M = 1 << N;
int dp[M][N]; //有n个点,点之间的距离为10e7
int w[N][N]; //存储点到点的距离
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i ++)
for (int j = 0; j < n; j ++)
cin >> w[i][j];
//因为存储的是最小值,所以dp应该首先初始化最大值
memset(dp, 0x3f, sizeof dp);
//起点在0号点,对应的状态是1(二进制状态下是1,00000000001,第0号点最右边的点,十进制表示1)
dp[1][0] = 0;
for (int i = 1; i < 1 << n; i ++) //枚举位数,使得每一位都变成0
// if (i & 1) //当最后一位出现0是不合法的,起点就是走过的点,需要将这个点排除出去
for (int j = 0; j < n; j ++) //枚举终点,他不是最后的一个点,只是停留点,需要往这个点靠拢
if (i >> j & 1) //停在j这个点上,代表已经走过了,检验这个状态也要合法,下面开始转移
for (int k = 0; k < n; k++)
if (i - (1 << j) >> k & 1) //检测第k这个点是否合法
{
//上面已经验证i状态下的第j位是1,而我们是从k跳到j的,所以不包含j,要减去j
dp[i][j] = min(dp[i][j], dp[i - (1 << j)][k] + w[k][j]) ;
}
cout << dp[(1 << n)- 1][n - 1];
return 0;
}