”我已经学会了如何活在当下。“
”怎么做?“男孩问。
”找一个安静的地方,闭上眼睛,然后呼吸。“
”听起来真不错,然后呢?“
”然后集中注意力。“
“集中在什么上?”
”蛋糕上。“ 鼹鼠回答。
二进制运算技巧
& :判断一个数字的二进制表示第 if (n & (1 << k))
| :将数 n = n | (1 << k)
~ :所有位都取反
状压DP
Eva学的状压DP,就是用一个二进制数来表示一个集合。
常见的状压DP分为两类,一种是有比较明显的结构的,如图、二维平面坐标等,另一种是较抽象的,比如物品等。
通常来说在压缩状态的基础上,我们还需要一个额外的信息来帮助我们转移,它通常会是关于”最后“的一个信息。
题目
洛谷P1433
题意:在二维平面上给
思路:大概就是,比如我现在走到了一个点,我关心的是,有哪些点我还没走过,从当前点往后怎么走最短,我并不关心前面是怎么走的。
设置状态 f[s][id]
,表示我走过了 s
里为 id
。我用当前这个状态取更新下一步,也就是说,假设与当前点 id
相连的点是 v
,那么f[s | (1 << v)][v] = min(f[s | (1 << v)][v], f[s][id] + w(id, v))
。
1 | struct point { |
洛谷P1879
给一个二维的矩阵,矩阵里为
思路:压缩行或者列,这里选取压缩行。如果我从上到下一行行考虑,我在考虑第 s & (s >> 1)
若结果 s & s_
,如果结果
状态设置:f[i][s]
,表示当前在处理的是第 s
。
1 |
|
又写了一版用当前行更新下一行的。
1 |
|
洛谷P3052
给
思路:枚举箱子编号,和哪些物品已经放过了,f[s][i]
表示已经放进箱子的物品的集合是 s
,最后一个箱子为
1 | int f[1 << 18][18], w[18]; |
洛谷P4802
有
思路:枚举已访问过的点集,和最后一个访问的点,来更新与该最后一个点相连的点,最终目标就是最后一个点是 f[1][0] = 0;
别的都是负无穷。
1 | int f[1 << 18][18]; |
洛谷P3092
有
思路:首先
1 |
|
总结
状压DP是一个优化爆搜的方式,爆搜我们有好多好多状态,状压DP可以把状态压到一起,然后一起转移,这就起到了优化的效果。
可以观察到的是,用状压解决的问题,被压的状态因为是指数级别的,所以不会很大,一般只有十几。
继续做题
洛谷P2704
给一网格图,网格图上有些位置可以放士兵,有些不可以,一个士兵的攻击范围是上下左右各两格,要求摆放士兵的时候不能在别人的攻击范围里,问最多能放多少个士兵。
思路:采取前面做题的思路,把一行的状态压起来,行内冲突提前预处理。不一样的是,这次不仅需要知道上一行的状态,需要知道上上行的状态,才能决定当前行能怎么放,所以定义状态为 f[i][s1][s2]
表示,当前处理到了第 T
, 所以在预处理的时候就把每个合法的状态里士兵的个数记下来。还有就是,这个DP数组,如果按之前的想法开需要 s
进行重编号,那要开多大呢?就是提前打个表数一下。还有这个判断一行的状态是否合法的时候,可以稍微优化一下。
打表如下:
1 | inline bool valid (int x) { |
打表结果是60。
1 |
|
2021女生赛C连锁超市
有
思路:看到这个数据范围,感觉压谁都不对……首先,我们来判断我们需要哪些条件来进行转移,首先我要知道哪些公司的红包我已经领过了,其次我还得知道,我现在在哪个景点,这将决定我接下来能往哪里走。知道自己需要什么以后,其实还是挺清晰的,刚开始我还在想要把景点压一起,但确实,其实我并不关心我走过哪些点,因为我只会沿着这个缆车往上走,我肯定不会经过重复的点。所以我们要压的是公司,问题是公司的数量跟景点的数量是一样的……呜呜呜,这我咋压。理性分析以后,我们可以发现,其实我们并不关心所有的公司,如果一个公司只有一个景点,那我想也不用想,走到就拿,不需要关心之前有没有走到过,因为必不可能,这样子想想,最差情况下,被压的公司数,就是点数的一半,啊!我能压了!对于初态,因为强制从
1 | int f[1 << 18][36], cnt[36], c[36], w[36], ans[36], id[36]; |
arc058E
问一个长度为 0~10
。有多少种满足:有一段区间,把它拆成三段后,三段的区间和从前往后依次为
思路:这题看起来跟状压也没啥关系……被Co老师喂了奇妙做法。其实这个问题可以转换成,问一个序列里,是否存在一个位置,满足以它结尾的后缀和里,包含 0 ~ x + y + z
是否在以 len
表示,后面的长度,所以我们需要预处理出来 10
的各个幂次。写到这里,再来明确一下状态,状态 f[i][s]
表示,当前处理到序列中第 0 ~ x + y + z
的数字的集合为 s
。目标状态是啥呢?就是包含 z, y + z, x + y + z
即代码里的 tar
。下一个头疼的就是,长度 +1 的时候,我的集合 s
要怎么变,首先之前所有的后缀和都要加上这个新加的数,并且新增了一个后缀和就是这个新加的数,需要注意的是,可能会有状态溢出 x + y + z
,一旦溢出了,我们在枚举状态的时候就枚举不到了,所以需要和全局最大的 S
与一下。
1 |
|
- Post title:bitmask 状压DP
- Post author:Eva.Q
- Create time:2022-06-29 14:34:05
- Post link:https://qyy/2022/06/29/Algorithm/bitmask/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.