题目描述:
n行n列的棋盘上放n个皇后,已知皇后之间不能同行、同列、同对角线,任意给定正整数n,求放置的方法与解决方案数量。
示例:
输入:n = 4
输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
算法思想:
典型的深度优先搜索例题。对于深度优先遍历,重要的是想清楚搜索的顺序。八皇后问题中,已知皇后之间不能同行,所以 按行枚举,则只需要记录每行皇后放置的列号即可。
则问题转变为枚举n列的全排列问题,类似 leetcode46 全排列的做法,对列号进行全排列,即可得到每个解决方案的列号,从而安要求解决问题。
c++代码示例:
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
this->n = n; // 设置全局变量n = n;
used = vector<bool>(n,false); // 标记列有没有用过初始化。
dfs(0);
vector<vector<string>> result; // 将ans中存储的每个解决方案中列号转化为题意要求的字符。
for(auto per : ans){
vector<string> res; // 定义中间字符串数组
for(int row = 0; row < n; row++){
string s(n,'.'); // 临时对象s初始化为 n 个'.'
int col = per[row]; // 记录对应列号,将其标记为'Q'
s[col] = 'Q';
res.push_back(s);
}
result.push_back(res);
}
return result;
}
void dfs(int u){
if(u == n){
ans.push_back(set);
/*
for(auto x:set) cout << x << ' ';
cout << endl;
*/
return;
}
for(int i = 0; i < n; i++){
// 如果第col列没有用过,并且不在同一对角线上,则选择这一列号
if(!used[i] && !dg[u + i] && !udg[u - i]){
set.push_back(i);
used[i] = dg[u + i] = udg[u-i] = true;
dfs(u + 1); // 递归进入下一列
used[i] = dg[u + i] = udg[u-i] = false;
set.pop_back();
}
}
}
private:
int n; // 全局变量 n
vector<int>set;
vector<vector<int>> ans;
unordered_map<int,bool> dg;
unordered_map<int,bool>udg;
vector<bool>used;
};
对 ans 内容打印可以得到:
ans:
1 3 0 2
2 0 3 1
可以看到在 \ 对角线上的元素的坐标和都相等, i + j 相等。
在 / 对角线上的元素的坐标差都相等,即 i - j 相等,为了防止出现i - j < 0,而越界的现象, 可以将其开成unordered_map<int,bool>的映射,这样就不用担心越界的问题了。
对于52题求解决方案数量更为容易,只需返回ans.size()即可。
代码示例:
class Solution {
public:
int totalNQueens(int n) {
this->n = n;
used = vector<bool>(n,false);
dfs(0);
return ans.size();
}
void dfs(int u){
if(u == n){
ans.push_back(set);
return;
}
for(int col = 0;col < n; col++){
if(!used[col] && !dg[u - col] && !udg[u + col]){
set.push_back(col);
used[col] = dg[u - col] = udg[u + col] = true;
dfs(u + 1);
used[col] = dg[u - col] = udg[u + col] = false;
set.pop_back();
}
}
}
private:
int n;
vector<int>set;
vector<vector<int>> ans;
vector<bool> used;
unordered_map<int,bool>dg;
unordered_map<int,bool>udg;
};