状压DP
状压(state compression)DP似乎不怎么常看到,其实也并不复杂。只是在一般的用整数作为状态索引 或者 中的 或者 变成了用二进制作索引。这样就能方便地列举在选择问题中指数个()状态了。
先来熟悉一下集合运算的位运算:
集合操作
位运算
intersection A∩B
A & B
![]()
union A∪B
A | B
![]()
complementary Aᶜ
~A
![]()
relative complementary A\B
A & (~B)
![]()
symmetric difference A△B
A ^ B
![]()
一些操作:
state =| (1<<i) # 从状态state加入i
state & ~(1<<i) # 从状态state去掉i
state & (1<<i) # 判断state是否包括i,或者写成(state>>i)&1
s &= s-1 # 最低位的 1 置成 0
p = s & -s # 获得最低位的1的位置(1&-1=1,2&-2=2)
s = state
while s:
yield s # 遍历state的非空子集s
s = (s-1) & state热身:子集(power set)
除了前面提到的用DFS求解,还可以枚举所有用整数的二进制表示来枚举nums的所有个子集(包括空集的power set)。然后在每个状态中判断是否各个数字被选中。
TSP
来看看经典的TSP(Traveling salesman problem):
给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。
如果城市的数量不多的话(n<10),那么我们就可以用状压DP来求解。具体地说,我们用 来表示身处 ,走过 (二进位数字)城市后最小花费。 的状态数一共有 个。我们把起点叫做 ,求解过程:
初始化: ,其余均等于
状态转移:
求解: ,等于到达城市 且遍历了所有城市的最小花费,加上返回到起点的最小花费。
这里用二进制来表示 ,再用DP实现最小花费计算,就是状压DP的过程。
参考代码:
路径问题
不同路径:有顺序的放球问题
不同路径 II:二维路径数(方案数)DP
不同路径 III:状态压缩DP
分割子集
这题的解法不是DP,而需要暴力搜索所有的分割子集方法。
枚举子集在搜索过程中不会出现重复选中的元素。这里和枚举子集不一样,因为每个元素最后都要落到某一个子集,所以有可能要重头搜索见参考代码中dfs(vis, target, 0, k-1)。所以用二进制的“状态“来表示每个元素是否已被选,就可以避开那些已经考虑过的元素了。
棋盘
如何将N个皇后放置在NxN的棋盘上,并且使皇后彼此之间不能相互攻击,共有多少种摆放方案。皇后能攻击到它上下左右,以及左上左下右上右下八个方向上任意一个格子。
N国王 [SCOI2005]互不侵犯
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
参考代码:
Last updated
Was this helpful?