状压 DP
一、什么是状压 DP?
状压 DP 是一类将「复杂状态」通过位运算压缩成一个整数,并在这些状态上进行动态规划的方法。
从动态规划角度看,动态规划常见有两种形式:
- 记忆化搜索(递归 + 记忆)
- 多阶段决策型 DP(由前一阶段转移到下一阶段)
- 使用 二进制数
- 用 0 / 1 表示某个对象的状态
- 二进制天然对应 "是 / 否"
- CPU 对位运算支持极快
- 一个
int就可以表示最多 $32$ 个状态单元 - 一个
long long可以表示最多 $64$ 个状态单元 - 问题需要保存"之前做过哪些选择"
- 且当前决策依赖这些历史选择
- 具有 最优子结构
- 点是否访问
- 格子是否放棋子
- 任务是否完成
- 元素是否被选中
- $n \leq 20$:非常安全
- $n \leq 22$:勉强可行
- $n \geq 25$:通常不可行
- 放棋子 / 不放棋子
- 相邻格子不能冲突
- 一行一行或一列一列推进
- 棋盘覆盖
- 放炮 / 放士兵 / 放国王
- "相邻不能同时为 1"
- 访问哪些点
- 当前在什么位置
- 顺序很重要
- 旅行商问题(TSP)
- Hamilton 路径 / 回路
- 原问题维度大
- 但某一维非常小
- 可以"指数换多项式"
- 一维状态:$dp_{mask}$
- 二维状态(最常见):$dp_{mask,i}$
- 从小集合到大集合: $$ dp_{mask \ | \ (1 \ll k)} \leftarrow dp_{mask} $$
- 从大集合到小集合: $$ dp_{mask} \leftarrow dp_{mask \ \oplus \ (1 \ll i)} $$
状压 DP 属于第二类:通过保存每一个阶段的"状态",由旧状态推导新状态,最终得到最优解或统计结果。
二、什么是「状态压缩」?
1. 状态压缩的本质
状态压缩就是:用尽可能小、尽可能高效的数据结构,表示一个状态。
在绝大多数状压 DP 中:
例如:
| 二进制位 | 含义 |
|---|---|
| 0 | 未选 / 未访问 / 未放置 |
| 1 | 已选 / 已访问 / 已放置 |
若有 $n$ 个对象,则一个 $n$ 位二进制数即可表示一个状态。
2. 为什么通常用二进制?
如果每个单元有 $3$ 种状态,也可以用三进制压缩,但实现与效率都会明显变差,竞赛中极少使用。
三、什么时候适合使用状压 DP?
状压 DP 的适用条件可以总结为以下四点:
1. 状态本身需要被保存(有 DP 属性)
2. 状态单元只有少数几种取值(通常是 0 / 1)
例如:
3. 状态单元数量较小
这是因为状态数为:
$$ \text{状态数} = 2^n $$
四、状压 DP 的典型应用场景
1. 棋盘 / 格子覆盖类问题
常见于:
2. 路径与排列问题(如 TSP)
经典代表:
3. $N$ 很小但状态复杂的问题
五、状压 DP 的基本状态设计
1. 常见 DP 形式
表示状态为 $mask$ 时的最优值 / 方案数
表示:
- 当前状态集合为 $mask$
- 额外信息是 $i$(如当前位置、最后选的点)
2. 状态转移的两种基本方向
六、典型「状压 DP 模板」解析(棋盘类)
int n;
int maxn = 1 << n; // 总状态数
for (int i = 1; i <= m; ++i) { // 枚举阶段(如第 i 行)
for (int j = 0; j < maxn; ++j) { // 当前状态
if (当前状态合法) {
for (int k = 0; k < maxn; ++k) { // 上一个状态
if (上一个状态合法 &&
当前状态与上一个状态不冲突) {
dp[i][j] = 转移(dp[i-1][k]);
}
}
}
}
}模板含义总结:
七、位运算基础(状压 DP 的数学工具)
1. 位运算符说明
| 运算 | 含义 | 特性 | |
|---|---|---|---|
& | 与 | 相同为 1 | |
| `\ | ` | 或 | 有 1 为 1 |
^ | 异或 | 不同为 1 | |
~ | 取反 | 0 ↔ 1 | |
<< | 左移 | ×2 | |
>> | 右移 | ÷2 |
2. 常用位运算技巧(非常重要)
① 判断第 $i$ 位是否为 1
if (x & (1 << (i-1)))② 将第 $i$ 位设为 1
x |= (1 << (i-1));③ 将第 $i$ 位设为 0
x &= ~(1 << (i-1));④ 删除最右侧的 1(经典)
x = x & (x - 1);常用于:
---
状压 DP 的典型问题分类与解题总结
一、路径 / 排列类问题(TSP / Hamilton)
1. 问题识别特征
看到以下关键词,高度怀疑是状压 DP:
本质特征:顺序重要 + 子集状态 + 终点有关
2. 常见题型
3. 典型状态设计
$$ dp_{mask,i} $$
含义:
4. 核心转移方程
从小集合到大集合:
$$ dp_{mask \ | \ (1 \ll j), j} = \min(dp_{mask,i} + cost_{i,j}) $$
或从大集合反推:
$$ dp_{mask,i} = \min(dp_{mask \ \oplus \ (1 \ll i), j} + cost_{j,i}) $$
二、棋盘覆盖 / 相邻约束类问题(行 DP + 状压)
1. 问题识别特征
题目常具有:
本质特征:局部约束 + 分阶段推进
2. 常见题型
3. 典型状态设计
$$ dp_{row,mask} $$
含义:
4. 合法性与转移
行内合法:
(mask & (mask << 1)) == 0行间不冲突:
(mask & prev_mask) == 0转移:
$$ dp_{row,mask} += dp_{row-1,prev\_mask} $$
三、匹配 / 配对类问题(小规模)
1. 问题识别特征
本质特征:集合中元素两两消除
2. 常见题型
3. 典型状态设计
$$ dp_{mask} $$
含义:
4. 核心转移思路
1. 找到 $mask$ 中第一个为 $0$ 的 $i$
2. 枚举另一个未配对的 $j$
3. 把 $i$ 和 $j$ 一起加入 $mask$
$$ dp_{mask \ | \ (1 \ll i) \ | \ (1 \ll j)} = \min(dp_{mask} + cost_{i,j}) $$
四、子集划分 / 分组类问题
1. 问题识别特征
本质特征:子集拆分 + 子集 DP
2. 常见题型
3. 典型状态设计
$$ dp_{mask} $$
表示:
4. 核心转移方式
枚举 $mask$ 的一个子集 $sub$:
$$ dp_{mask} = \min(dp_{mask \ \oplus \ sub} + cost_{sub}) $$
其中 $sub \subseteq mask$ 且 $sub$ 合法。
【前置知识点】
1、跳跃DP入门
【后置知识点】
1、概率与期望DP
【思维导图】

【题目知识点分类】
01
Matching
普及+/提高
--
练习
02
Traveling Salesman among Aerial Cities
普及+/提高
--
练习
03
徒競走
普及+/提高
--
练习
04
Get Everything
普及+/提高
--
练习
05
General Weighted Max Matching
普及+/提高
--
练习
06
Chain Contestant
普及+/提高
--
练习
07
GEPPETTO
普及+/提高
--
练习
08
糖果
普及+/提高
--
练习
09
补给
普及+/提高
--
练习
10
Cows in a Skyscraper G
普及+/提高
--
练习
11
吃奶酪
普及+/提高
--
练习
12
和風いろはちゃん
普及+/提高
--
练习
13
Make 10 Again
普及+/提高
--
练习
14
Fish
普及+/提高
--
练习
15
Equalization
普及+/提高
--
练习
16
Cube
普及+/提高
--
练习
17
售货员的难题
普及+/提高
--
练习
18
拯救莫莉斯
普及+/提高
--
练习
19
Eating
普及+/提高
--
练习
20
Snakes
普及+/提高
--
练习