版权声明:本文为博主原创作品, 转载请注明出处! https://blog.csdn.net/solider98/article/details/83210638
思路分析:
本题是典型的二进制状态压缩DP问题(为方便叙述, 约定点i表示编号为i的点, 整数的最低位为第0位), 设F[i][j]表示从点0的点出发到点j, 且经过点的状态为i的最短路径长(路径上所有边的权值之和), 是如何表示经过点的状态的? 如果i的第k(k >= 0)位为1那么该路径经过点k, 为0则不经过点k, 最终需求得F[(1 << n) - 1][n - 1] 欲求F[i][j], 可考察其对应路径的倒数第2个顶点(如果存在)t, 初始化F[1][0]为0, 下面给出状态转移方程:
注意: i & ~(1 << j)表示将i的第j位赋0得到的值, weight[t][j]表示边<t, j>(如果存在)的权值
F[]i][j] = min{ F[i & ~(1 << j)][t] + weight[t][j] | t j, i 的第j位为1, i的第t位为1, 存在边<i, j>}
下面给出AC代码:
//CH0103_最短Hamilton路径
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 20;
int F[1 << MAX][MAX], weight[MAX][MAX];
int main(){
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j) scanf("%d", &weight[i][j]);
memset(F, 0x3f, sizeof(F)), F[1][0] = 0;//前者将F中所有元素初始化为正无穷大, 是必要的
for(int i = 2; i < (1 << n); ++i)
for(int j = 0; j < n; ++j)
if((i >> j) & 1)
for(int t = 0; t < n; ++t)
if(j != t && (i >> t) & 1)
F[i][j] = min(F[i][j], F[i & ~(1 << j)][t] + weight[t][j]);
cout << F[(1 << n) - 1][n - 1] << endl;
return 0;
}
值得指出的是, 对于F[i][j], 上述算法递增的计算考察i值, 也即考察当前问题时, 所有的子问题(对应更小的i值)已经求解
对于上述算法的时间复杂度, 显然为O()