LeetCode 52
题目简述
N皇后问题的变式
难度:困难
描述:
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回 n 皇后不同的解决方案的数量。
示例:
输入: 4
输出: 2
解释: 4 皇后问题存在如下两个不同的解法。
[
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
题解
凑巧最近也要系统地学习回溯法了,我就把之前的这道题翻出来看了看
准备写个题解
常规的 N皇后解题代码会显得更为复杂
但在这道变式面前显得不那么必要,毕竟题目只是要求计算个数不是要求列举所有解法
但实话实说自己当时根本没有合适的思路,用N皇后问题的原解勉强莽了一个AC,在翻看评论区时发现了一个高点赞的代码
实不相瞒我甚至看了两遍都没太看懂
直到用纸笔认认真真演算了一遍后才豁然开朗,心里就一个词——
牛逼!
只能说这种思维高度真的需要长时间的培养和一些突如其来的灵感的
今天我来重新为这个代码梳理一遍思路(我感觉和我一样刚开始看得懵逼的人会很多)
实现代码
class Solution {
public:
int solu = 0;
void dfs(int n, int row, int col, int m_dia, int a_dia) {
if(row == n) {
solu++;
return;
}
int free = ~(col | m_dia | a_dia) & ((1 << n) - 1);
while(free) {
int curr = free & -free;
dfs(n, row + 1, col | curr, (m_dia | curr) << 1, (a_dia | curr) >> 1);
free &= free - 1;
}
}
int totalNQueens(int n) {
dfs(n, 0, 0, 0, 0);
return solu;
}
};
解析
这个思路的核心解法在于DFS + 位运算剪枝
思路机制如下:
- 使用常规 DFS 层层搜索
- 用三个整型记录每一层的哪个格子可以放置皇后,三个整型分别代表列,左斜下,右斜下 (col, m_dia, a_dia)
- 两个核心位运算
x & -x 代表除最后一位 111 保留,其它位全部为 000
x & (x - 1) 代表将最后一位 111 变成 000
函数:
int totalNQueens(int n) {
dfs(n, 0, 0, 0, 0);
return solu;
}
递归调用DFS
而函数DFS中:
语句:
int free = ~(col | m_dia | a_dia) & ((1 << n) - 1);
获取当前行可放置的皇后位置
语句:
while(free) {
int curr = free & -free;
dfs(n, row + 1, col | curr, (m_dia | curr) << 1, (a_dia | curr) >> 1);
free &= free - 1;
}
获取上一行的皇后约束条件
并通过位运算约束即将进行DFS的下一行
由此循环,直到满足语句:
if(row == n) {
solu++;
return;
}
累加一次可行解,获取递归出口
以上就是该N皇后变式问题的核心思路
位运算的细节我就不赘述了
个人觉得拿纸笔认真推演一遍应该是很好懂的