[状压dp] 最短Hamilton路径(模板题+状压dp)

0. 前言

状压 dp 就是采用二进制数保存状态,方便进行位运算操作。例如 八皇后、八数码问题也都是采用了状态压缩的思想来使用一个二进制数唯一对应集合中的一个状态。

关键是要体会采用二进制数来表示状态的思想,要转变传统思维,学习接收并吸收这种思想。

1. 状压dp 模板题

91. 最短Hamilton路径

在这里插入图片描述
这个问题如果暴力来做的话,首先需要确定走过 0~n-1 这些点的顺序,这个就是 n!,然后再求长度,是 n,那么总共就是 n!*n 时间爆炸…


状压 dp 思路:

重点: 状压 dp

思路:

  • 状态定义:
    • f[i][j]:所有从 0 号点走到 j 号点,走过的所有点状态压缩为 i 的所有路径的最小值。
    • 状压方式为:因为只有 20 个点,即将 i 看作一个二进制数,取其前 20 位,每一位 01 代表这个点是否走过
  • 状态转移:
    • 分类依据:所有路径都是从 0 走到 j 的,中间走过的所有点是 i。可以以当前路径的倒数第二个点进行分类,有:
      • 倒数第二个点是第 0 号点
      • 倒数第二个点是第 1 号点
      • 倒数第二个点是第 2 号点
      • 倒数第二个点是第 n-1 号点
      • 则这样就能够不重不漏的将整个状态划分完毕
    • 状态转移方程f[i][j] = f[i - (1 << j)][k] + w[k][j]
  • 状态初始化:f[1][0]=0,第 0 个点走向自身的距离为 0。由于取 min,故其它 f[i][j]=inf
  • 返回答案f[(1 << n) - 1][n - 1] 走完 n 个点(即 0~n-1 二进制数位均为 1),且走到第 n-1 个点的最小值即为答案

本题非常非常非常经典,是状压 dp 入门经典题目,不论思路还是代码都是很好想的。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 20, M = 1 << N;

int n;
int w[N][N];
int f[M][N];

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);
    f[1][0] = 0;
    
    for (int i = 0; i < 1 << n; ++i)            // 枚举所有路径
        for (int j = 0; j < n; ++j)             // 枚举所有点
            if (i >> j & 1)                     // 如果路径包含该点
                for (int k = 0; k < n; ++k)     // 枚举转移的点
                    if ((i - (1 << j)) >> k & 1)// 除去j点,一定包含k这个点,才能走通
                        f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j]);
    
    cout << f[(1 << n) - 1][n - 1] << endl;     // 走完n个点,且走到第n-1个点的最小值即为答案
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值