【原创】状态压缩动态规划入门(状压dp)

状态压缩动态规划简称状压dp,是一种通过二进制+dp的方法来解决问题的算法,一般来说这类算法适用于有n个任务,将n个任务全部完成需要的最小时间等(也可以用于棋盘问题等),此时我们需要构造2n2^n2n种状态来,考虑各个状态再得出答案。状态压缩动态规划其实算是一种相对暴力的算法,其时间代价往往是指数级别的(速度还是比普通暴力法快很多),掌握状态压缩动态规划首先需要掌握位运算,这是状态压缩动态规划的基础。位运算:1.& :参加运算的两个数据,按二进制位进行“与”运算。运算规则: 0&0=
摘要由CSDN通过智能技术生成

状态压缩动态规划简称状压dp,是一种通过二进制+dp的方法来解决问题的算法,一般来说这类算法适用于有n个任务,将n个任务全部完成需要的最小时间等(也可以用于棋盘问题等),此时我们需要构造 2 n 2^n 2n种状态来,考虑各个状态再得出答案。
状态压缩动态规划其实算是一种相对暴力的算法,其时间代价往往是指数级别的(速度还是比普通暴力法快很多),掌握状态压缩动态规划首先需要掌握位运算,这是状态压缩动态规划的基础。

位运算:
1.& :参加运算的两个数据,按二进制位进行“与”运算。
运算规则: 0&0=0; 0&1=0; 1&0=0; 1&1=1

2.| :参加运算的两个对象,按二进制位进行“或”运算。
运算规则: 0|0=0; 0|1=1; 1|0=1; 1|1=1

3.^ :参加运算的两个数据,按二进制位进行“异或”运算。
运算规则: 0^0=0; 0^1=1; 1^0=1; 1^1=0

4.~ :参加运算的一个数据,按二进制进行“取反”运算。
运算规则: ~1=0 ;~0=1

5.<< :定义:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。
设 a=1010 1110,a = a<< 2 将a的二进制位左移2位、右补0,即得a=1011 1000。(最高位不为1时,每左移一位相当于乘以2)

6 >> :定义:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。

算法步骤:
状态压缩动态规划算法基本可以分为三步:(以c++为例)
1.计算共有多少任务,建立数组;

vector<vector<int>> dp(n,vector<int>(1<<num_of_mission));

2.遍历所有情况,进行位运算来表达所有的情况;
3.建立动态规划方程。

例题
我们得到了一副藏宝图,藏宝图显示,在一个迷宫中存在着未被世人发现的宝藏。

迷宫是一个二维矩阵,用一个字符串数组表示。它标识了唯一的入口(用 ‘S’ 表示),和唯一的宝藏地点(用 ‘T’ 表示)。但是,宝藏被一些隐蔽的机关保护了起来。在地图上有若干个机关点(用 ‘M’ 表示),只有所有机关均被触发,才可以拿到宝藏。

要保持机关的触发,需要把一个重石放在上面。迷宫中有若干个石堆(用 ‘O’ 表示),每个石堆都有无限个足够触发机关的重石。但是由于石头太重,我们一次只能搬一个石头到指定地点。

迷宫中同样有一些墙壁(用 ‘#’ 表示),我们不能走入墙壁。剩余的都是可随意通行的点(用 ‘.’ 表示)。石堆、机关、起点和终点(无论是否能拿到宝藏)也是可以通行的。

我们每步可以选择向上/向下/向左/向右移动一格,并且不能移出迷宫。搬起石头和放下石头不算步数。那么,从起点开始,我们最少需要多少步才能最后拿到宝藏呢?如果无法拿到宝藏,返回 -1 。

示例 1:
输入: [“S#O”, “M…”, “M.T”]

输出:16

解释:最优路线为: S->O, cost = 4, 去搬石头 O->第二行的M, cost = 3, M机关触发 第二行的M->O, cost = 3, 我们需要继续回去 O 搬石头。 O->第三行的M, cost = 4, 此时所有机关均触发 第三行的M->T, cost = 2,去T点拿宝藏。 总步数为16。
在这里插入图片描述
示例 2:

输入: [“S#O”, “M.#”, “M.T”]

输出:-1

解释:我们无法搬到石头触发机关

示例 3:

输入: [“S#O”, “M.T”, “M…”]

输出:17

解释:注意终点也是可以通行的。

限制:

1 <= maze.length <= 100
1 <= maze[i].length <= 100
maze[i].length == maze[j].length
S 和 T 有且只有一个
0 <= M的数量 <= 16
0 <= O的数量 <= 40,题目保证当迷宫中存在 M 时,一定存在至少一个 O 。

题解:
一个人在迷宫中,要从起点 SS 走到终点 TT。迷宫有两类特殊点,分别是:

MM:机关点,需要用石头触发
OO:石头点,一次可以搬一块石头
只有当所有 MM 点均被触发以后,终点才可到达,问起点走到终点的最小代价。

实际上,我们的走法只有四种:
1.从 S走到O
2.从O走到M
3.从M走到O
4.从M走到T

首先我们先要算出从S走到O再走到M所需的最短距离,在计算出从M走到最近的O再走到下一个M的最短距离,将其储存后再进行dp。(即先处理数据,得到最短路径)
我们使用nj(机关的数量)来建立状态压缩dp方程,我们定义 f ( m a s k , i ) f(mask,i) f(mask,i)表示当前在第 i 个 M 处,触发状态为 mask 的最小步数,如果当前 mask 代表的已触发集合为 T,未触发集合为 U - T,则我们可以推出这样的动态规划转移方程:
在这里插入图片描述
其中 m a s k x o r 2 i maskxor2^i maskxor2i 表示把 M i M_i Mi 已触发的集合当中去掉,即 mask 这个状态可以由 m a s k x o r 2 i maskxor2^i maskxor2i 状态转移得到,转移时我们除了关注触发状态 mask 的变化,我们还关注是从哪一个 M 转移到了 M i M_i M

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员毛师傅

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

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

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

打赏作者

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

抵扣说明:

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

余额充值