核心逻辑与数学原理
状态压缩动态规划(State Compression DP,简称状压 DP)的核心本质是将一维具有强离散特征的组合集合,通过数制转换(通常为二进制)无损映射为一个低维的整数标量。这种降维映射能够让我们在 $O(1)$ 的时间内通过位运算进行状态的物理转移与冲突校验。
旅行商问题(TSP, Travelling Salesman Problem)是状压 DP 的教科书级模型:给定一个图及两两节点之间的距离,求一条经过所有顶点恰好一次且总权值最小的回路(本节讨论其变型:求从起点 0 触发,经过所有顶点恰好一次到达终点 $N-1$ 的最小权值路径)。
1. 状态空间的离散映射
在 TSP 问题中,我们不仅需要知道当前身处哪一个城市,更需要知道已经访问过了哪些城市,还剩下哪些城市没有访问。
- 指数级空间的坍塌:若用传统集合存储访问过的城市,状态判定极其低效。由于每个城市只有“访问过”与“未访问”两种互斥的边际状态,我们可以用一个长度为 $N$ 的二进制串来精确复现这个状态。
例如:$N=5$ 时,二进制状态 $S = (10110)_2$: - 从右往左数(第 0 位到第 4 位),第 1、2、4 位为 1,第 0、3 位为 0。
- 这代表城市 1、2、4 已经被访问过,城市 0、3 尚未访问。
此时,这个集合状态被完美压缩为了一个十进制整数:$S = 2^1 + 2^2 + 2^4 = 2 + 4 + 16 = 22$。
2. 位运算控制矩阵与状态转移的算子化
借助计算机底层的位运算,集合的交、并、补、属于等操作可以高度抽象为以下算子:
| 集合操作 | 位运算数学表达式 | 物理语义 |
|---|---|---|
| 判定属于 | (S >> i) & 1 |
检查城市 $i$ 是否已被访问(是否为 1) |
| 集合并入 | S | (1 << i) |
将城市 $i$ 强行标记为已被访问 |
| 集合消除 | S & ~(1 << i) |
将城市 $i$ 从已访问集合中剔除 |
| 全集表达 | (1 << N) - 1 |
得到一个包含 $N$ 个 1 的全选状态 |
状态设计与算法推导
1. 状态空间定义
设状态 $f[S][i]$ 表示:当前已经访问的城市集合为 $S$(其二进制掩码对应的十进制整数),且当前正站在城市 $i$ 上时,所花费的最小总距离。
- 合法性限制:状态 $f[S][i]$ 存在物理意义的先决条件是:城市 $i$ 必须已经包含在集合 $S$ 中,即满足
(S >> i) & 1 == 1。
2. 状态转移方程推导
我们通过逆向寻找当前状态的前驱来实施决策。在当前身处城市 $i$、集合为 $S$ 之前,上一步我们一定身处另一个不同的城市 $j$。
由于从 $j$ 跨步到了 $i$,那么在上一步时,城市 $i$ 一定处于未访问的状态。因此,前驱状态的城市集合必然是 $S - \{i\}$,在位运算中表达为 S ^ (1 << i)。
$$\forall j \in S \setminus \{i\}, \quad f[S][i] = \min \left( f[S][i], f[S \setminus \{i\}][j] + \text{weight}(j, i) \right)$$
代入位运算算子:
f[S][i] = min(f[S][i], f[S ^ (1 << i)][j] + weight[j][i]);
3. 拓扑序的控制控制
由于大集合的状态总是由小集合(少一个元素的集合)状态推导而来,在底层的双重循环中,外层循环必须严格从小到大枚举状态数 $S$(从 1 到 $(1<
NOIP 实战避坑指南
1. 位运算优先级混乱导致逻辑瘫痪
if (s >> i & 1 == 1) 这样的判定。在 C++ 运算符优先级中,算术运算符与关系运算符(如 ==)的优先级严格高于移位运算符(>>)和按位与(&)。上面这行代码在编译时会被等价解读为 if (s >> (i & (1 == 1))),直接导致状态校验产生荒谬的越界或恒为假。if (((s >> i) & 1) == 1)。2. 空间维度开辟错误引发内存爆表(MLE)
f[MAXN][1 << MAXN]。在进行状态访问时,由于把第一维和第二维写反,导致在处理 $N=20$ 的数据时,内存申请并没有问题,但在跑表 f[s][i] 时,由于 $s$ 达到了 $10^6$,直接引发段错误(数组越界内存非法读取)。或者错将维度开成 f[1<<25][25],直接导致编译期 内存超限(Memory Limit Exceeded)。f[1 << 20][20],以保证内存局部性与读写速度。
经典 NOIP/洛谷 真题
1. 洛谷 P1433 吃奶酪
dist[i][j]。状态设计为 f[S][i] 表示已吃掉的奶酪集合为 $S$,当前停留在第 $i$ 块奶酪上的最短距离。完全套用 TSP 递推模版,最后全局扫描 $\min_{1 \le i \le N} \{ f[(1<2. 洛谷 P1879 [USACO06NOV] Corn Fields G
f[i][S] 表示已经决策完前 $i$ 行,且第 $i$ 行的放牧状态对应的二进制掩码为 $S$ 时的总方案数。
(S & (S << 1)) == 0。(S & S_pre) == 0。
通过行与行之间的动态滚动交织,将指数级的棋盘组合数优化到线性递推。