【学习笔记】技巧-状态压缩
【学习笔记】技巧-状态压缩
第一篇学习笔记
前置知识:位运算
(位运算一定记得加括号)!!!
基本知识
意义
使用二进制数的每一位(0/1)代表一个元素的状态(是/否)
譬如:\((1001)_2\) 代表第1、4个元素为True
一般可以用于表示元素是否已经选了、或者是否开关
特征
由于类型大小限制,压缩的元素数目不能多于20个左右
因此最显著的特征为:数据范围中一个维度很小,只有不到20
lowbit:返回二进制最低位的1所代表的值
ll lowbit(ll x)
{
return x&-x;
}
遍历一个状态的所有子集
for(ll temp=state;;temp=state&(temp-1))
{
//实体代码
if(!temp)
break;
}
奇技淫巧
<<和>>完成位移,&和|判断障碍物(P2704 [NOI2001] 炮兵阵地)
主要题型
1. 状压dp
2. 容斥
3. 前缀和
状压dp
分为 计数型(不可重) 和 最值型(可重)
两种写法:
- 人人为我:其他状态来更新我(正常dp比较常用,但是有可能TLE)
- 我为人人:我更新其他状态(复杂度
应该好一点)
计数型(不可重)
最大的难点在于去重。
如果是分组问题,可能出现同一种分组情况被不同的最后一组更新。(例如 \(\{1,2\} \{3,4\}\) 被 \(\{1,2\}\) 和 \(\{3,4\}\) 重复计算了两次)一个可行的方法是:转移时,只允许用包含大状态lowbit的那一组转移,由于lowbit只有一个,所以相同的分组方案只会转移一次。
例如:仅当(cur&state)==cur且(cur&lowbit(state))!=0时,dp[state]+=dp[state^cur](cur表示当前组,state表示总状态)
最值型(可重)
不用去重,简单不少。
值得注意的是,最值问题有时候也会出错。
如果当前状态和转移来源的编号(或者其他信息)有关,就不能只写dp[state],而是要写dp[state][i],比如 P1433 吃奶酪
这是我dp没学好
容斥
状态压缩的非常规应用。有些时候非常有用
遍历 \(2^n-1\) 也就是 \((111...1)_2\) 的所有子集,如果 \(popcount(temp)\%2==1\) 那么 ans+=当前状态和 ,否则ans-=当前状态和
这个方法可以快捷地完成容斥。
前缀和
- 人人为我:枚举每个状态的子集,加到自己上(比较慢,容易TLE)
- 我为人人:如果自己不为0,枚举包含自己的集合,将自己加到它(比较快)

浙公网安备 33010602011771号