压状DP
状压 DP - OI Wiki (oi-wiki.org)一般通过状态压缩将状态以整数的二进制形式来表示,1
表示改位置背选择,0
表示没有被选择。
例题: P 1896 [SCOI 2005] 互不侵犯 - 洛谷 | 计算机科学教育新生态 (luogu. Com. Cn)题目大意:在 N*N 的棋盘里面放 K 个国王(1<=N<=9,1<=K<=N*N),使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共 8 个格子。
思路:
f[i][j][l]
表示第 i
行的状态为 sit[j]
,且棋盘上已经放了 l
个国王的合法方案数。sit[j]
的二进制中 1
就表示这个位置上放国王,0
表示不放。j
的最大值就是单行最大的合法方案数,sit[j]
包含了单行的所有情况所以可以提前求出来sta[j]
表示 sit[j]
的状态下需要多少个国王。即 sit[j]
中 1
的个数。- 状态转移方程
f[i][j][l]=sum(f[i-1][x][l-sta[j]])
遍历 x
,即单行合法状态数量,当然 sta[x]
满足国王数量的限制
例题:
P 5911 [POI 2004] PRZ - 洛谷 | 计算机科学教育新生态 (luogu. Com. Cn)题目大意: 有 n
个人需要过桥,第 i 的人的重量为 w[i]
,过桥用时为 t[i]
,这些人过桥时会分成若干组,只有在某一组的所有人全部过桥后,其余的组才能过桥。桥最大承重为 W
,问这些人全部过桥的最短时间。100≤W≤400 ,1≤n≤16,1≤t≤50,10≤w≤100
思路: 同样使用状态压缩:i
的二进制中某一位为 1
表示这个人在这一组内,和大家一起过河,按照这样分组 共有 1<<n-1
种分组 情况,每种情况的状态压缩刚好对应一个整数。
ts[i]
表示第i
组(也就是 i 的二进制下1
位为一组的情况)的情况下过桥需要的最长时间。ws[i]
表示第i
组总体的体重。- 如何判断第
j
个人是否在第i
组,i&(1<<j)==1
则表示第j
个人在第i
组。 - 先完成上述的分组,在对所有的组采用 DP 求出最优的解,这时遇到一个为题,即 第
i
组和第 j
组成员很有可能有重复的,那如何避免这样的情况呢。 - 换个思路解决这个问题,既然第
i
组和第 j
组成员很有可能有重复的,那我们就 让其必然出现重复的,这样就可以 看成第 i
组是从第 j
组增加一组得来的,但于此有引入一个问题就是 要使的 i
的二进制要完全包含 j
的二进制,即 j
为 1
的位,i
也必为 1
,并且要求出 i
为 1
而 j
不为 1
的位对应的组,以及两者都为 1
的组。 - 既然这两个问题都是因二进制而生,那必然还需要用二进制运算解决,解铃还须系铃人。
- 首先令
j=i-1
,则解决第一个问题,此时j
为1
的位i
必为1
。 - 令
j=i&j
,与运算两者都为 1 结果为 1
,反之为 0
。此时的 新j
就是重复的队员所组成的小组。 - 在 第二步的基础上,两组按位异或
i^j
,相同为零,相异为一。表示 i
组有而 j
组没有的队员所组成的小组,即新增的队员组成的小组。 - 然后不断重复
1
到3
步即可,直到j
为0
。 - 总的来说:
i
组是在i&(i-1)
组的基础上再加上i^(i&(i-1))
组得到的。 - 这两个问题看着很绕,但其是解决他们只需要的在
dp
时对for
内进行一些修改即可:
整体代码: