[AcWing] 91. 最短Hamilton路径(C++实现)状态压缩dp例题

1. 题目

在这里插入图片描述

2. 读题(需要重点注意的东西)

思路:

状态压缩:即用二进制表示状态
用状态压缩表示哪个位置被用过了
如有如下五个节点
0 1 2 3 4
则 state = 10011 ,表示第0、1、4个节点被使用了

怎么用状态压缩求出最短路径呢?
我们用二进制表示各个节点选与不选的情况,然后计算每个节点,如果选了它,则进行dp:

f[ state ][ j ] = f[ state_k ][ k ] + weight[ k ][ j ](该式见如下dp分析

dp分析
在这里插入图片描述


代码实现思路:
① 对于每一个状态i,枚举每一个节点j,如果选择了j这个节点
② 在每一个节点去除当前 j 的状态后,遍历满足条件 i >> k & 1 == 1的k
③ 然后求f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j]),找出从 k 到 j 的最短路径
④ 最后输出f[(1 << n) - 1][n - 1],表示枚举完了所有的2的n次方减1种状态,最终节点到n-1的最短路径,即为最终答案。

3. 解法

---------------------------------------------------解法---------------------------------------------------

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

using namespace std;

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

int n;
int w[N][N];
int f[M][N]; // f[i][j] 表示状态为i的情况下,停在点j,此时的最短路径是多少

// f[state][j] = f[state_k][k] + weight[k][j]
// state_k = state 除掉j之后的集合,且state_k要包含k
// state怎么来表示一个集合呢? ---> 状态压缩

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;
    
    // 用二进制枚举所有的状态 ..0000、..0001、..0010、..0011、...
    // 每一个点都有用或不用两种状态,因此有2的n次方种状态
    // ①
    for (int i = 0; i < 1 << n; i ++ )
        for (int j = 0; j < n; j ++ )
            // 判断i的第j位是否为1,i右移j为与1
            // 为1表示经过了该点
            // ②
            if (i >> j & 1)
                for (int k = 0; k < n; k ++ )
                    if (i >> k & 1)
                    	// ③
                        // i - (1 << j) 表示 i 除去 j 后的状态
                        f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j]);
	
	//④
    cout << f[(1 << n) - 1][n - 1]; // 表示把所有状态都遍历过了,然后停在第n-1位

    return 0;
}

可能存在的问题(所有问题的位置都在上述代码中标注了出来)

4. 可能有帮助的前置习题

5. 所用到的数据结构与算法思想

  • 动态规划
  • 状态压缩dp

6. 总结

状态压缩dp的例题,理解思想并自行推导出代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Cloudeeeee

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值