状压DP学习笔记[浅谈]
状压 DP 是动态规划的一种,通过将状态压缩为整数来达到优化转移的目的。
by OI Wiki
前语
对于状压,一打开洛谷的题单你就可以看到满屏的蓝和紫,是不是很吓人,显然就是很吓人
状压题目一般有一个同性,就是数据范围不会特别大,一般卡在 16−2016-2016−20 之间,这就是一个非常显然的提示了,当你发现爆搜无从下手或者肯定会炸的时候,就可以开始考虑状压DP了。
状压叫做状压的原因和他的状态设计有关,如果是背包状态大多是前几件物品放多少空间可获得的最大价值,而状压的状态中,有一个条件一般是由二级制表示的,后续在例题中可以快速理解状压状态的定义。
前置知识
在学习状压之前要先学习几种二级制下的位运算。
- &:与运算,在二进制状态下 110010111001011100101 & 100100110010011001001 = 100000110000011000001 即两位都为1返回111否则皆返回000,位数不足向后对齐,其余位数补000.
- |:或运算,在二进制状态下 110010111001011100101 & 100100110010011001001 = 110110111011011101101 即两位只要有一位为111返回111否则皆返回000,位数不足向后对齐,其余位数补000.
- ^:异或运算,在二进制状态下 110010111001011100101 & 100100110010011001001 = 010110001011000101100 即两位不相同返回111否则皆返回000,位数不足向后对齐,其余位数补000.
知道了上述运算后,即可开始入门状压DP。
例题
P1896 [SCOI2005] 互不侵犯 (来自洛谷)
题目大意:
在 N×NN \times NN×N 的棋盘里面放 KKK 个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共 888 个格子。
分析
对于本题,我们将设置的状态应当可以说明各个位置是否放有国王,按照正常思路,我们会开一个数组,然后在其中对应位置赋值为 000 或 111 来表示该位置是否有国王,但是这很浪费空间,所以就有了状压,状压便将这一个数组变成了一个 010101 组成的二进制数,因为二进制数的唯一性,所有每一个二进制数都可以变成唯一的一个十进制整数由此,大大减小了空间上的开支。
状态设计
按照 dp 的套路,我们设状态:dp[i][j][S] 表示选到了第 i 行,第 i 行的状态为 S,用了 j 个国王的最优方案个数;
那么如何转移呢?我们知道国王的攻击范围是周围的 888 个格子,所以这些格子是不可以放其他国王的,应该在状压枚举的时候避开,那么如何避开呢,由于我们提前用 SSS 表示了国王的位置状态,所以可以根据 SSS 来对国王可放位置进行判断。
至于如何判断,那么就可以使用到上面铺垫的位运算了。
如果在一个国王的正下方有一个国王,那么 S1S1S1 & S2S2S2 肯定 !=0!=0!=0 好理解吧。那么如果在左下角或者右下角呢?那就位移一下就好了啊 <<<<<< 与 >>>>>> 可以分别将一个数的二进制形态向左或右位移一格(向左最右边会补0),所以又可以写出新的式子。由此行外判断部分便解决了。
同理,在行内也需要通过左右位移数列来判断当前方法的合法性。即如果相邻的两个都放了国王将数列向右移再与原数列做 & 运算必不等于 000 。
到这里为止,本题的大致内容都已经展现了,剩下的就是基本的动态规划转移推到,请读者自行推导或滑到本文最下方阅读题解。
总结
状压DP是Noip及更高级别竞赛常考内容,需要多加联系以巩固提高。
下面给出以下比较好的练手题目:
- 互不侵犯(洛谷)
- 最长上升子序列(洛谷)
- PRZ
- Genshin Impact Startup Forbidden III
本人实力有限,如有不足欢迎大佬们提出。