N皇后-回溯
- N皇后
- 题目
- 示例
- 分析
- 代码
- N皇后II
- 题目
- 示例
- 分析
- 代码
- 总结
N皇后
题目
LeetCode 51.N皇后
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例
示例1
输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例2
输入:n = 1
输出:[["Q"]]
分析
给定n,题目要求的就是0-n-1这n个数的满足要求的所有排列方式,需要满足的条件就是,对于任意两个棋子,不能存在同行、同列、同对角线的情况。
从第0行开始,需要在[0, n-1]中选择一个数,在对应位置放置棋子,然后是第1行、第2行…直至第n-1行
显然,题目的本质就是一个排列问题,只是在这些排列方式中,需要去除某些不符合要求的情况,排列问题可以用回溯算法求解,确定求解方法为回溯。
定义一个回溯函数,backtrack(vector &board, int row), 其中board为二维数组,记录当前棋局,row为当前处理行。
在backtrack函数内部,需要注意一些细节:
- 终止条件是row == n,即所有行都已经处理完,这时当前的棋盘就是一个合法的解,加入结果集中
- 为了去除某些不合要求的排列,每次对位置做选择时,先要判断一下,用一个isValid函数来判断当前位置是否存在同行、同列、同对角线的情况,如果存在,continue
回溯算法框架
res = []
backtrack(棋局,选择列表):
if 满足终止条件:
res.push_back(棋局)
return
for 选择 in 选择列表:
做选择
backrack(棋局,选择列表)
撤销选择
代码
class Solution {
public:
vector<vector<string>> res;
vector<vector<string>> solveNQueens(int n) {
vector<string> board(n, string(n, '.'));
backtrack(board, 0);
return res;
}
void backtrack(vector<string> &board, int row) {
int n = board.size();
if (row == n) {
res.push_back(board);
return;
}
for (int i = 0; i < n; i++) {
if (!isValid(board, row, i)) continue;
board[row][i] = 'Q';
backtrack(board, row + 1);
board[row][i] = '.';
}
}
bool isValid(vector<string> &board, int row, int col) {
int n = board.size();
for (int i = row; i >= 0; i--) { //上方
if (board[i][col] == 'Q') return false;
}
for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) { //左上方
if (board[i][j] == 'Q') return false;
}
for (int i = row, j = col; i >= 0 && j < n; i--, j++) { //右上方
if (board[i][j] == 'Q') return false;
}
return true;
}
};
N皇后II
题目
LeetCode 52.N皇后II
n 皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。
示例
示例1
输入:n = 4
输出:2
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:1
分析
与上道N皇后完全一样,不过只需要记录排列方式的总数,而不用存储具体的排列方式
代码
class Solution {
public:
int res = 0;
int totalNQueens(int n) {
vector<vector<int>> board(n, vector<int>(n, 0));
backtrack(board, 0);
return res;
}
void backtrack(vector<vector<int>> &board, int row) {
int n = board.size();
if (row == n) {
res++;
return;
}
for (int i = 0; i < n; i++) {
if (!isValid(board, row, i)) continue;
board[row][i] = 1;
backtrack(board, row + 1);
board[row][i] = 0;
}
}
bool isValid(vector<vector<int>>& board, int r, int c) {
int n = board.size();
for (int i = 0; i < r; i++) {
if (board[i][c]) return false;
}
for (int i = r, j = c; i >= 0 && j >= 0; i--, j--) {
if (board[i][j]) return false;
}
for (int i = r, j = c; i >= 0 && j < n; i--, j++) {
if (board[i][j]) return false;
}
return true;
}
};
总结
扩展:如果仅要求不能同行同列,不要求不能对角线,那这题完全就是n个数的全排列问题