原题目链接:https://leetcode-cn.com/problems/n-queens-ii/
这个题目与leetcode 51 题唯一不同在于输出不同,本题只要求输出几种解法
51题要求输出每种解的具体形式
上篇文章用回溯法和基于集合的回溯法解决 51 题,这次使用位运算来解决
为了更好理解代码后注释的1,2,3 表示步骤,下面会模拟程序执行过程逐步进行解释。
class Solution:
def solveNQueens(self, n: int):
def dfs(row, col, pie, na):
"position 中 1 表示可以放皇后的位置"
"p 中 1 表示放皇后的位置"
"col,pie,na, 中 1 表示受上一行皇后影响该行不能放皇后的位置"
"pie,na 不符命名规则,但是方便理解 撇,捺"
"pie 右上角到左下角,na 左上角到右下角"
if row == n: # 1
return 1
res = 0 # 2
position = (~(col | pie | na)) & ((1 << n) - 1) # 3
while position: # 4
p = position & (-position) # 5
position &= position - 1 # 6
res += dfs(row + 1, col | p, (pie | p) << 1, (na | p) >> 1) # 7
return res
return dfs(0, 0, 0, 0)
"""
与 & 逢 0 为 0
或 | 逢 1 为 1
取反 ~ 1 变 0, 0 变 1
左移 <<
右移 >>
以 4 为例 4 的二进制 0100
1 的二进制 0001
4 & 1 -> 0100 & 0001 = 0000
4 | 1 -> 0100 | 0001 = 0101
~4 -> ~0100 = 1011
4 << 1 -> 0100 << 1 = 1000
4 >> 1 -> 0100 >> 1 = 0010
以 n=4为例:
dfs(0,0,0,0)从步骤1开始
1. 行数不等于n,不进入
2. res=0
3. position = (~( 0 | 0 | 0 )) & (( 1 << n )-1)
(~0) & (1 0000 - 0000 1) -> 1111 & 1111 -> 1111
position = 1111 含义:在第一行的四个位置上都可以放皇后
|√|√|√|√|
|-|-|-|-|
|-|-|-|-|
|-|-|-|-|
4. position 不为0 进入循环
5. p = 1111 & 0001
p = 0001 含义:此时在第一行最后一个位置上放置皇后
|-|-|-|Q|
|-|-|-|-|
|-|-|-|-|
|-|-|-|-|
6. position &= position - 1
1111 & (1111 - 0001) -> 1111 & 1110
position = 1110 含义:第一行还有前三个位置可以放置皇后
|√|√|√|Q|
|-|-|-|-|
|-|-|-|-|
|-|-|-|-|
7. 进入第二层 dfs(1, 0001, 0010, 0000)
这里 col pie na 就详细计算一次,下文不再详细展示
col | p = 0000 & 0001 = 0001
(pie | p) = (0000 & 0001) <<1 = 0001 << 1 = 0010
(na | p) = (0000 & 0001) >> 1 = 0001 >> 1 = 0000
dfs(第二行,最后一个位置,第三个位置, 第五个位置(超出所以是0))
1. 行数不等于n,不进入
2. res = 0
3. position = (~(0001 | 0010 | 0000)) & (( 1 << n)-1)
通过 | 将第二行不能放皇后的情况重合到一起 0011代表第二行后两个位置都不能放置皇后
|-|-|×|×|
(~0011) & (1111) -> 1100 & 1111
position = 1100 含义:第二行前两个位置可以放置皇后
|√|√|√|Q|
|√|√|×|×|
|-|-|-|-|
|-|-|-|-|
4. position 不为零 进入循环
5. p = 1100 & 1011
p = 1000 含义:第二行第一个位置放皇后
|√|√|√|Q|
|Q|√|×|×|
|-|-|-|-|
|-|-|-|-|
6. position &= position - 1
1100 & 1011 -> 0100
position = 0100 含义:第二行第二个位置可以放置皇后
7. 进入第三层 dfs(2, 1001, 0100, 0100)
1.不进入
3. position = (~(1001 | 0100 | 0100)) & ((1 << n)-1)
通过 | 将第三行不能放皇后的情况重合到一起 1101代表第三行行只有第三个位置可以放
|×|x|√|x|
(~1101) & (1111) -> 0010 & 1111
position = 0010 含义:只有第三个位置可以放
|√|√|√|Q|
|Q|√|×|×|
|×|x|√|x|
|-|-|-|-|
5. p = 0010 & 1110
p = 0010 含义:第三行第三个位置放置皇后
|√|√|√|Q|
|Q|√|×|×|
|×|x|Q|x|
|-|-|-|-|
6. position &= position - 1
0010 & (0010 -1 ) -> 0010 & 0001
position = 0000 含义:第三行已经没有位置可以放置皇后了
7. 进入第四层 dfs(3, 1011, 1100, 0011)
3. position = (~(1011 | 1100 | 0011)) & ((1 << n)-1)
此时已经可以看出 第四行已经没有合适位置放置皇后了
因为,col | pie | na = 1111 |×|x|x|x|
(~1111) & (1111)
position = 0000 含义:没有位置可以放置皇后了
|√|√|√|Q|
|Q|√|×|×|
|×|x|Q|x|
|×|x|x|x|
4. position = 0 不进入循环 此时开始退出递归 返回到第三行
但是第三行中position = 0000 表明也没有可以放置的地方
(或者说只有一个地方可以放,已经放过了),继续返回到第二层
此时第二层的position = 0100 表示第二层还有一个地方可以放皇后,
返回到第二层将原来的Q放在第二个位置上
|√|√|√|Q| |√|√|√|Q|
|Q|√|×|×| |x|Q|×|×|
|-|-|-|-| |-|-|-|-|
|-|-|-|-| |-|-|-|-|
第二层原来的方法 回溯到第二次重新放
然后继续循环
"""
s = Solution()
print('n = 4 时有', s.solveNQueens(4), '种解')
print('n = 5 时有', s.solveNQueens(5), '种解')
print('n = 8 时有', s.solveNQueens(8), '种解')