Description
在 $n \times n\ (1 \leq n \leq 9)$ 的棋盘上放置 $m\ (0 \leq m \leq n \times n)$ 个国王,每个国王都能攻击周围的 $8$ 个格子,求使它们无法互相攻击的方案数。
Source
Solution
状压DP 入门题,当然也可以打表(滑稽
如何 状态压缩?
考虑把每一行的状态用一个 二进制数 来表示。若这个数的某一位为 $1$,则代表这个位置上放了国王,$0$ 反之。
状态:
用 $f_{i,j,k}$ 表示第 $i$ 行是第 $j$ 个状态,前 $i$ 行(包括第 $i$ 行)共放了 $k$ 个国王的方案数。
初始:
其中 $cnt_i$ 表示第 $i$ 个状态国王的个数。若在第 $1$ 行状态合法,则此状态的方案数为 $1$ 。
转移:
其中 $cnt_k$ 表示第 $k$ 个状态国王的个数。若第 $i - 1$ 行是 第 $j$ 个状态,第 $i$ 行是 第 $k$ 个状态,枚举 第 $j$ 个状态 和 第 $k$ 个状态,如果这两个状态合法,且 第 $k$ 个状态 能够放在 第 $j$ 个状态 的下一行(两个状态的国王保持和平),就可以转移。假设在前 $i$ 行共放了 $l$ 个国王,那么前 $i - 1$ 行就放了 $l - cnt_k$ 个国王,显然这个方案是成立的,所以累加更新这个状态的方案数。
怎么判断 两个状态合法 且 上下两行国王和平 呢?这就要用到 状态压缩 时常用的位运算了。
若第 $i$ 个状态为 $state_i$,且 $state_i\& \left( state_i << 1 \right) > 0$,则说明存在相邻的国王互相攻击,该行状态不合法。
若第 $i$ 个状态为 $state_i$,第 $j$ 个状态为 $state_j$,且 $state_i\& state_j > 0 $,则说明存在上下国王互相攻击。
若第 $i$ 个状态为 $state_i$,第 $j$ 个状态为 $state_j$,且 $state_i\& \left(state_j << 1\right) > 0$,则说明存在左上与右下的国王互相攻击。
若第 $i$ 个状态为 $state_i$,第 $j$ 个状态为 $state_j$,且 $\left(state_i << 1\right) \& state_j > 0$,则说明存在右上与左下的国王互相攻击。
答案:
其中 $tot$ 表示合法的状态总数。答案即为:最后一行状态合法,前 $n$ 行共放了 $m$ 个国王的方案数总和。
一行里所有的状态共 $2^n$ 个(考虑每一位是 $0$ 还是 $1$),所以时间复杂度为 $O(nm2^{2n})$,可能会超时。但很容易发现,在一行里有很多状态本来就是不合法的,比如 $n = 4$ 时 $0110$ 这个状态,实际有用的状态远不及 $2^n$ 个。因此我们考虑先用搜索预处理出所有合法的状态,再进行转移,这样效率能高很多。
Code
1 |
|