LCP 76-魔法棋盘
在大小为 n * m
的棋盘中,有两种不同的棋子:黑色,红色。当两颗颜色不同的棋子同时满足以下两种情况时,将会产生魔法共鸣: -
两颗异色棋子在同一行或者同一列 - 两颗异色棋子之间恰好只有一颗棋子 > 注:异色棋子之间可以有空位
由于棋盘上被施加了魔法禁制,棋盘上的部分格子变成问号。chessboard[i][j]
表示棋盘第 i
行 j
列的状态: - 若为 .
,表示当前格子确定为空 - 若为 B
,表示当前格子确定为 黑棋 - 若为 R
,表示当前格子确定为 红棋 - 若为 ?
,表示当前格子待定 现在,探险家小扣的任务是确定所有问号位置的状态(留空/放黑棋/放红棋),使最终的棋盘上,任意两颗棋子间都 无法
产生共鸣。请返回可以满足上述条件的放置方案数量。 示例1: > 输入:n = 3, m = 3, chessboard = ["..R","..B","?R?"]
> > 输出:5
> > 解释:给定的棋盘如图:
![image.png](https://pic.leetcode.cn/1681714583-unbRox-
image.png){:height=150px} > 所有符合题意的最终局面如图:
![image.png](https://pic.leetcode.cn/1681714596-beaOHK-
image.png){:height=150px} 示例2: > 输入:n = 3, m = 3, chessboard = ["?R?","B?B","?R?"]
> > 输出:105
提示: -n == chessboard.length
-m == chessboard[i].length
-1 <= n*m <= 30
-chessboard
中仅包含"."、"B"、"R"、"?"
本题视频讲解
见【力扣杯2023春·个人赛】 第五题。
思路
注:题意中的「两颗异色棋子之间恰好只有一颗棋子」允许中间有空格子。
分类讨论,对于一行或者一列的 RB 序列,有以下 7 种情况:
- 空。
- 只有一个 B。
- 只有一个 R。
- 连续多个 B。
- 连续多个 R。
- BR 交替,且以 B 结尾。
- BR 交替,且以 R 结尾。
为什么单独一个 B/R 也算一个状态?因为既可以变成连续多个 B/R,又可以变成 BR 交替。而「连续多个 B/R」和 「BR 交替」是无法互相转换的。
遍历棋盘的过程就是在不断生成序列的过程,那么向序列末尾添加 B 或者 R,这些状态就会互相转换,形成一个 7\cdot 2 的转换关系表,用数组 trans 记录,其中 -1 表示非法转换。(见代码)
由于有 7 种情况,可以用 3 个比特存储,所有列合并在一起就需要 3m 个比特,这可以用一个二进制数 mask 来存储。
写一个记忆化搜索,DFS}(r,\textit{mask}) 表示枚举到第 r 行,每列的 RB 序列的状态组合起来是 mask 时,继续向后枚举可以得到多少合法的方案。
考虑从下一行的 DFS}(r+1,\textit{mask}’) 转移过来,如何枚举 mask}’ 呢?
写一个暴力搜索来枚举,枚举第 r 行怎么放棋子是合法的。定义 dfs}(c,\textit{rowMask},\textit{colMask}) 表示搜索到第 c 列,当前行的 RB 序列状态是 rowMask,每列的 RB 序列的状态组合起来是 colMask:
- 如果当前符号是 B,则往当前行和当前列的末尾添加 B,根据 trans 来看是否合法,如果合法就继续搜索。
- 如果当前符号是 R,则往当前行和当前列的末尾添加 R,根据 trans 来看是否合法,如果合法就继续搜索。
- 如果当前符号是 ?,则往当前行和当前列的末尾添加 R、添加 B、或者什么也不加,根据 trans 来看是否合法,如果合法就继续搜索。
- 如果当前符号是 .,继续搜索。
- c=m 时,表示对于当前行,搜索到一个合法的方案,也就是 mask}’=\textit{colMask,那么可以继续递归 DFS}(r+1,\textit{mask}’)。
当 r=n 时,表示找到了一个合法的方案,返回 1。
递归入口:DFS}(0,0)。
注:代码实现时,如果 n<m 就旋转棋盘,这样可以保证 m\le 5。
其它语言稍后补充。
1 | TRANS = ( |
1 | // 每一行的含义:{当前序列末尾添加 B 后的状态,当前序列末尾添加 R 后的状态} |
复杂度分析
- 时间复杂度:\mathcal{O}(n21^{m})。这里保证 m\le 5\le \sqrt{nm(如果 n<m 则旋转棋盘)。动态规划的时间复杂度 = 状态个数 \times 单个状态的计算时间。这里状态个数为 \mathcal{O}(n7^{m}),单个状态的计算时间为 \mathcal{O}(3^{m}),因此时间复杂度为 \mathcal{O}(n7^{m}3^{m})=\mathcal{O}(n21^{m})。考虑到很多状态是非法的,在全为 ? 的情况下,dfs 最多调用 5601518 次。
- 空间复杂度:\mathcal{O}(n8^{m})。为了方便用位运算,实际用了 \mathcal{O}(n8^{m}) 的空间来存储状态。