状态压缩笔记
状态压缩小结
一般的DP
最长公共子序列 \(f_{i, j}\)表示\(a_{1 \to i}\)和\(b_{1 \to j}\)的最长公共子序列
背包 \(f_{i, j}\)表示前\(i\)个物品选一些,总体积为\(j\)时的最大价值
但当问题变得很复杂,有\(n\)个不同的属性那么就多开几维,大力出奇迹,就需要状态压缩了
状态压缩
举个🌰(栗子)
题目
1 | n个人玩传球游戏:球可以从任何一个人开始传,每次传给一个还没有接到过球的人,接到球的人继 |
用搜索是可以过的,但要记忆化,然而这篇小结是状态压缩
DP做法
设\(n\)个人的编号是\(0,1,\dotsi,n-1\)
\(f_{S,i}\)表示\(S\)集合中的人已经接过球,且当前球在第\(i\)个人手中的最小总代价
状态转移
算\(f_{S,i}\)时,枚举传球给\(i\)的人\(j\),\(S\)二进制的\(2𝑗\)位必须是1
\(f_{S-2^i,j} + cost_{j,i}\to f_{S,i}\)
代码
1 | //初始化 |
例题
题目 | 解法 | 易错点 |
---|---|---|
K.过桥 | \(S\)的第\(i\)位为1表示第\(i\)位队员已过桥。 | |
N.送外卖 | \(f_{S,i}\): 表示已经访问了集合 \(S\) 中表示的城市,且当前在城市 \(i\) 的最小时间 | 需要使用\(Floyd\)预处理所有点对之间的最短路径 |
O.单词游戏 | \(f_{S,i}\): 表示当前已经使用的单词集合是 \(S\),并且最后一个使用的是第 \(i\) 个单词时的最大价值,calc_cost计算重叠部分 | 许多细节,详见下方 |
P.玉米地 | \(f_{i, S}\) 表示处理到第 \(i\) 行,且第 \(i\) 行状态为 \(S\) 的方案数 | \(MODMODMOD\)!!!!! |
Q.慢跑小路 | 要满足“每条边至少走一遍,且起点等于终点”,需要构造一个欧拉回路 | 需要\(Floyd\)预处理 |
部分例题详解
O.单词游戏
需要一个calc_cost计算重叠部分 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18int calc_cost(string a, string b) {
int len_a = a.size();
int len_b = b.size();
int max_len = 0;
int f[12][12] = {};
for (int i = 0; i < len_a; i++) {
for (int j = 0; j < len_b; j++) {
if (a[i] == b[j]) {
if (i == 0 || j == 0)
f[i][j] = 1;
else
f[i][j] = f[i-1][j-1] + 1;
max_len = max(max_len, f[i][j]);
}
}
}
return max_len;
}
还要预处理存下来
1 | for (int i = 0; i < n; i++) { |
Q.慢跑小路
需要统计奇度数节点 1
2
3
4int p[16], js = 0;
for (int i=1;i<=n;i++)
if (deg[i]%2 == 1)
p[js++] = i;
注意结果输出还要加边的总长
printf("%d\n", edge+f[(1<<js)-1]);
还有多组数据的初始化
memset(deg,0,sizeof(deg));
memset(f,0x3f,sizeof(f));
不会有人就错在这里吧 :)
\(THE\) \(END\),作者:\(Hcz\)