题解/算法 {1064. 小国王}
LINK: https://www.acwing.com/problem/content/1066/
;
给定
n
∗
n
n*n
n∗n的矩阵, 如果一个位置上放棋子 则其相邻的8个位置上 不可以有棋子;
求一共放置
K
K
K个棋子的方案数;
用状态来表示, 每个位置用0/1
来表示, 1表示该位置上放置棋子, 0表示该位置为空;
如果按照传统的遍历矩阵
0 1 2 3 4
5 6 7 8 9
a b c d e
比如, 当前枚举到c
这个位置, 他的上个状态 即012...9ab
这个子区域, c
要判断的是b, 6, 7, 8
这4个位置;
.
即对于一个区域, 我们需要记录他末尾的n
的位置 他的状态用二进制表示1<<n
就够了;
.
那么, Dp[ n*n][ 1<<n][ n*n]
即Dp[ i][ st][ k]
表示: [0,1,...,i]
这些元素 最后的n
个位置 状态为n
, 且总共[0,1,2,...,i]
这些元素里 1
的个数;
.
答案为Dp[ n*n -1][ 所有状态][ K]
之和;
.
但这样的时间复杂度是1e9
;
这种方式, 你是依次遍历的每个元素, 但是状态存储的是 n
个元素, 即记录的是一行的状态;
其实, 既然你状态存储的是一行元素, 那么, 你可以直接遍历一行元素, 即把一整行元素 当成是一个元素;
.
把一整行元素 当成的一个元素, 这1个元素 有若干种放法 (原来1个元素 只有2种可能: 放/不放), 你可能认为他有1<<n
个放法 其实不是的, 这里可以优化, 即只有不含连续的1 才可能是合法的放法, 这不多的 最多才100个;
一个元素 他的可能集合 和 放法, 不是相同的;
.
放法, 要看pre_st
; 不同的pre_st
会决定cur_st
的合法性;
.
而可能集合, 他与pre_st
无关; 换句话说, 所有合法的cur_st
(可用于状态转移的 就是合法的; 即存在一个pre_st
使得pre_st 结合 cur_st
是合法的) 一定属于 可能集合; 但可能集合 不一定就是合法的;