状压 DP & 轮廓线 DP & 插头 DP 笔记
我本以为我很会这玩意的,不就是将集合取或不取设为状态,几个集合转移而已嘛!然后我就被一群题爆杀了。
一、普通状压
1. P2157 [SDOI2009] 学校食堂
首先有个结论:
虽然人数很多,但事实上关心第
那么有三种选择:
- 让
打饭,
f[i + 1][s >> 1][j - 1 + 8] = min(f[i + 1][s >> 1][j - 1 + 8], f[i][s][j + 8]);- 让
后面的人打饭,注意需要需要满足 都能容忍 先打饭。
f[i][s | 1 << k][k + 8] = min(f[i][s | 1 << k][k + 8], f[i][s][j + 8] + (t[i + j] ^ t[i + k]));- 特殊情况,
前面没有任何人打过饭。
f[i][s | 1 << k][k + 8] = min(f[i][s | 1 << k][k + 8], f[i][s][j + 8]);时间复杂度
2. P3092 [USACO13NOV] No Change G
首先看数据范围,肯定是对硬币状压,那么只需要知道每种硬币组合能不能买完所有物品,再在每组合法的硬币组合中取一个剩的钱最多的就好了。
设
时间复杂度
二、轮廓线 dp
有时候会发现,在二维表格上,
1. P7171 [COCI 2020/2021 #3] Selotejp
理解上面的“轮廓线”后,设
- 不能贴,直接更新。
f[x][y][s >> 1] = min(f[x][y][s >> 1], f[i][j][s]);- 如果左边一格存在,是能贴的,是横着走的,那就可以接着横着贴;否则也可以自己从头开始横着贴。
if (y and ch[i][j] == '#' and !((s >> m - 1) & 1))
f[x][y][s >> 1] = min(f[x][y][s >> 1], f[i][j][s]);
else
f[x][y][s >> 1] = min(f[x][y][s >> 1], f[i][j][s] + 1);- 竖着贴也是差不多的。
if (x and (s & 1))
f[x][y][s >> 1 | (1 << m - 1)] = min(f[x][y][s >> 1 | (1 << m - 1)], f[i][j][s]);
else
f[x][y][s >> 1 | (1 << m - 1)] = min(f[x][y][s >> 1 | (1 << m - 1)], f[i][j][s] + 1);时间复杂度
2. P4363 [九省联考 2018] 一双木棋 chess
首先,棋盘的形状肯定是靠在左上角的一个锯齿状的东西,考虑从最左下角的点的左边开始,
然后博弈论得是倒着取最优,所以要把棋盘中心对称旋转。
至于转移顺序,可以发现放一个棋子,就相当于交换了状态中的一组相邻的
时间复杂度
三、插头 DP
1. 多条回路
对于一条回路,回路上的每个格子的四条边,必然只有两条边和回路接触(一进一出)。那么借用轮廓线 DP 的思路,设

那么,状态数为

观察轮廓线,只有绿色的需要存是否存在向下方向,黄色的需要存是否存在向右方向。
假设都只用一位二进制表示,向左表示
注意:一般的状压,二进制最低位(最右边)对应从左往右第一列,本图为了便于观察,二进制最左边对应从左往右第一列。

可以发现:原本取本列(从
四、最小斯坦纳树
1. AT_abc395_g [ABC395G] Minimum Steiner Tree 2
我觉得先理解这个题还更容易理解模板啊!
有两个非固定点,考虑枚举每个第一个非固定点,把他当作固定点,预处理其对应所有第二个非固定点的答案,
设
对于固定
for (int s = 1; s < 1 << k + 1; s++) {
for (int i = 1; i <= n; i++)
for (int t = s; t; t = s & (t - 1))
f[s][i] = min(f[s][i], f[t][i] + f[s ^ t][i]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
f[s][i] = min(f[s][i], f[s][j] + dis[j][i]);
}对于第二个非固定点是 f[(1 << k + 1) - 1][i],离线存储下来。
时间复杂度
2. P6192 【模板】最小斯坦纳树
这时,把非固定点理解成“跳板”即可。
五、根号分治 + 状压
1. P2150 [NOI2015] 寿司晚宴
发现
当处理完大质数相同的时候,合并进总答案。
总复杂度
2. P8292 [省选联考 2022] 卡牌
如果质数个数较小,只需大力容斥即可,用总方案减去所有不合法的方案:钦定覆盖
但是质数个数很多,但又不算多,根据 NOI2015 寿司题经验,可以发现
考虑只有大质数怎么做,那么直接枚举给出的卡牌序列里整除大质数
接下来只需要把第一段和第三段结合起来,即对每种不同的小质数集合都算一次大质数的答案,容斥即可。