chenchen题解:最短Hamilton路径

题目描述:

blablablablablablablablablabla 传送门

算法思想:

这道题是状压dp的典型题
状态表示:
dp[i][j]表示从0j点所有经过点为i的二进制形式的最短Hamilton路径的长度。
数i的二进制形式表示点0 ~ n - 1每个点是否被走过
例如 i = 10,二进制表示为1010,表示1、3号点走过,0、2号点没有走过
状态计算:
考虑要走到j点之前,要经过的前一个点(设其编号为k),将集合分为n份,取最小值
dp[i][j] = min(dp[i - {j}][k] + a[j][k])
初始状态:
经过编号为0的点到达0点,此时的最短距离为0,dp[1][0] = 0
因为求最小值,所以将其它值设置为正无穷

时间复杂度:

O ( n 2 2 n ) O(n^2 *2^n)

代码实现+注释:

#include <iostream>
#include <cstring>
using namespace std;
const int N = 21, M = 1 << N;
int w[N][N], f[M][N];
int main(){
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            scanf("%d", &w[i][j]);
    //求最小值,f[][]初始化为正无穷
    memset(f, 0x3f, sizeof f);
    //初始状态,经过编号为0的点到达0点,此时的最短距离为0
    f[1][0] = 0;
    //从0 ~ 2^n 枚举所有经过点的状态集合
    for(int i = 0; i < 1 << n; i++)
        for(int j = 0; j < n; j++) {//遍历当前点
            // i >> j & 1 计算i的第j位是否为1
            if(i >> j & 1){//如果集合i中包含j
                //枚举所有转移到j的点k
                for(int k = 0; k < n; k++){
                    //保证i中包含点k
                    //i - (1 << j) 将状态i中去掉点j
                    if((i >> k & 1) && j != k)//计算i - (1 << j)的第k位是否为1
                        f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j]);
                }
            }
        }
    //最终答案为,经历过0 ~ n - 1的每个点后到达点n - 1时的最短距离
    printf("%d\n", f[(1 << n) - 1][n - 1]);
    return 0;
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43403405/article/details/107660021