n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例 1:
输入:n = 4
输出:[[".Q…","…Q",“Q…”,"…Q."],["…Q.",“Q…”,"…Q",".Q…"]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:[[“Q”]]
提示:
1 <= n <= 9
皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。
N皇后问题是典型的回溯问题, 解决回溯问题最好的方法就是先能画出树状图,这道题的画法是这这样的如图:
每一列通过for循环进行遍历,每一行通过递归进行遍历,这里画不下了,知道怎么画就行了;
那么这道题只要找到最后的叶子节点,就是答案之一了;
递归结束条件通过画图也很容易发现,只要递归的层数row等于n时,就到了叶子节点了;
然后就可以通过for循环确定每一列column,递归确定每一行row,这样通过行列是否满足棋盘规则就可以确定每个位置能不能放皇后Q了;
这个棋盘规则也很简单,就是三点
1,不能同行
2,不能同列
3,不能同斜线 (45度和135度角)
这里面我们其实并不需要对行进行检查,因为每一次的递归,行数都会相应增加,所以在一次次递归中,行的检查就已经通过列的检查覆盖完了;
其他就没有什么难度了,这道题只要能想到这个图怎么画的,知道行和列的关系就没有问题了;
详细注释在代码里,可以比较着看
代码如下:
class Solution {
private:
vector<vector<string>> ans;
//n是棋盘大小,row是递归到的行数
void backTracking(int n, int row, vector<string>& chessboard) {
//当行数为棋盘的最后一行时,递归结束
if (row == n) {
ans.push_back(chessboard);
return ;
}
//for循环是对每一列column进行的遍历,就是类似于宽度,
//递归则是对每一行row进行的遍历,及深度的遍历
for (int column = 0; column < n; ++column) {
//如果该行row该列column这个位置合法,就可以放皇后Q
if (isValid(row, column, chessboard, n)) {
chessboard[row][column] = 'Q';
backTracking(n, row + 1, chessboard);//递归
chessboard[row][column] = '.';//回溯
}
}
}
//判断是否合法
bool isValid(int row, int column, vector<string>& chessboard, int n) {
//检查第column列有没有Q出现
for (int i = 0; i < row; ++i) {
if(chessboard[i][column] == 'Q')
return false;
}
//检查左斜线有没有Q(45度)
for (int i = row - 1, j = column - 1; i >= 0 && j >=0; --i, --j) {
if (chessboard[i][j] == 'Q')
return false;
}
//检查右斜线有没有Q(135度)
for (int i = row - 1, j = column + 1; i >= 0 && j < n; --i, ++j) {
if (chessboard[i][j] == 'Q')
return false;
}
return true;
}
public:
vector<vector<string>> solveNQueens(int n) {
vector<string> chessboard(n, string(n, '.'));
backTracking(n, 0, chessboard);
return ans;
}
};
这种问题就属于棋盘类的问题,有一道相似的题目可以尝试一下:37. 解数独
这个题目其实是在原集合里进行的修改,所以不需要定义额外的变量,可以尝试一下;
带有详细注释的代码贴上:
class Solution {
private:
bool backTracking(vector<vector<char>>& board) {
//分别对行和列进行遍历,递归则是对这9个数进行的遍历
for (int i = 0; i < board.size(); ++i) {
for (int j = 0; j < board[0].size(); ++j) {
//如果这个位置有数,就跳过
if (board[i][j] != '.') continue;
//对9个数进行遍历
for (char ch = '1'; ch <= '9'; ++ch) {
//当前的数符合要求的话,就放到当前位置
if (isVaild(i, j, ch, board)) {
board[i][j] = ch;
//当递归到底找到了合适的一组后返回
if (backTracking(board)) return true;
board[i][j] = '.';//回溯
}
}
return false;//试完了9个数,都不满足,则放回false
}
}
return true;//若结束了遍历没有返回false,则说明找到了合适的答案
}
bool isVaild(int row, int column, char ch, vector<vector<char>>& board) {
//判断列是否有重复
for (int i = 0; i < 9; ++i) {
if (board[i][column] == ch)
return false;
}
//判断行是否有重复
for (int i = 0; i < 9; ++i) {
if (board[row][i] == ch)
return false;
}
//针对每一个小九宫格,判断里面是否出现重复的元素
int startRow = (row / 3) * 3;
int startColumn = (column / 3) * 3;
for (int i = startRow; i < startRow + 3; ++i) {
for (int j = startColumn; j < startColumn + 3; ++j) {
if (board[i][j] == ch)
return false;
}
}
return true;
}
public:
void solveSudoku(vector<vector<char>>& board) {
backTracking(board);
}
};
在力扣上一看还有一个N皇后||,和N皇后可以说一模一样,小改动一下就可以了,感觉比N皇后简单;
代码如下:
class Solution {
private:
int ans = 0;
void backTracking(int n, int row, vector<string>& chessboard) {
if (row == n) {
ans++;
return ;
}
for (int column = 0; column < n; ++column) {
if (isVaild(n, row, column, chessboard)) {
chessboard[row][column] = 'Q';
backTracking(n, row + 1, chessboard);
chessboard[row][column] = '.';
}
}
}
bool isVaild(int n, int row, int column, vector<string>& chessboard) {
for (int i = 0; i < row; ++i) {
if (chessboard[i][column] == 'Q')
return false;
}
for (int i = row - 1, j = column - 1; i >= 0 && j >= 0; --i, --j) {
if (chessboard[i][j] == 'Q')
return false;
}
for (int i = row - 1, j = column + 1; i >= 0 && j < n; --i, ++j) {
if (chessboard[i][j] == 'Q')
return false;
}
return true;
}
public:
int totalNQueens(int n) {
vector<string> chessboard(n, string(n, '.'));
backTracking(n, 0, chessboard);
return ans;
}
};
注意:下面啥也不是
评论区看到了一个面向测试编程🙈,击败百分百
代码如下(java)
class Solution {
public int totalNQueens(int n) {
int[] rs = new int[]{0,1,0,0,2,10,4,40,92,352,724,2680};
return rs[n];
}
}