【学习小结】状压dp

刚刚啃了一下午+一晚上状压dp,感觉找到了一点儿套路,练习还是很不够。

先来稍微总结一下,自己关于状压dp的理解。

什么是状压dp?

状压dp==状态压缩+dp

状态压缩

我们常常遇到一个问题状态非常多的情况,不仅情况复杂,状态多,还有很多限制条件。

这样无论是时间上很难跑过,空间上也难以

这种情况下,我们需要将大量的状态压缩为一个数字,最常见的就是二进制状态压缩

举个例子,常用0,表示空,1表示被占用。

如果有一排8个格子,可以用二进制为八位的数来表示它每个格子是否被占用,而我们在数组里面只需要储存对应的十进制数,遍历的时候也可以直接遍历到这种状态。

dp

dp还是一样,要满足子问题最优,无后效性,也有写dp方程,但是状压dp的入门级难度的dp定义和状态转移方程都比较套路

1、如果当前行对上方t行有影响 我们则定义 dp[i][j][k]······[z]表示第i行,当前行选状态j,i-1行是状态·······i-t+2行状态是z的答案,影响几行,dp就有多少维,但是第一维是用于表示第几行的,一般也就影响两行,三行。

2、转移方程。

求方案数(此处我们以三维为例):dp[i][j][k]+=dp[i-1][k][s](k和s都需要枚举),输出答案:∑dp[n][i][j](枚举第n行)

求最值:dp[i][j][k]=max{dp[i-1][k][s]}。输出答案:max(dp[n][i][j])

3、我们需要预处理出前t行的答案

预备知识(套路)

这部分可以跳过不看

下面这一部分比较二,因为位运算这东西懂的人一眼看穿,不懂的就只能靠输出来看,惨,啃得就很慢,我就属于后者,所以就写详细一点儿了。

1、基本左右移:x<<k,x右移k位。x>>k,x左移k位。(一般用于初始化二进制地图,判断同一行的状态是否冲突)

2、二进制地图 mp[i]=(mp[i]<<1)+e[i][j].相当于存下每一行支持放置的位置。向左移一下==腾出来一个空位给新的格子

3、满状态:(假设一行有m个格子)(1<<m)-1,比如m=3时,左移三位变为1000,-1后变为111,刚好成了三个格子都选的状态

4、(i为当前枚举的状态)(i<<1)& i 判断i这一行是否满足了无相邻的格子同时被选用。为什么把i自己移了一下就可以判断了?举个例子 i = 0110,左移后变为1100,1100和0110在第三位都为1了(相当于原数的第三位和第二位都为1了),所以此状态不合法。

同理 (i<<2)& i ,可以判断是否是每隔两个格子才被选用的状态 

5、 j & mp[i] ==j 用来判断原地图的第i行是否支持j这种状态,同理 k&j 就可以判断k和j这两种状态是否冲突了

入门题目

首先熟悉一下模板

传送门

然后稍微做一下变式

互不侵犯传送门

最后学习一下减状态的思想,以及更高维dp的处理

炮兵阵地传送门

“Make my parents proud,and impress the girl I like.”
原文地址:https://www.cnblogs.com/NSD-email0820/p/9521591.html