[回溯法] – 46 - 全排列 - python + Java
[回溯法] – 47 - 全排列Ⅱ - python + Java
N皇后
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
例如,上图为 8 皇后问题的一种解法。给定一个整数 n,返回 n 皇后不同的解决方案的数量。
输入: 4
输出: 2
解释: 4 皇后问题存在如下两个不同的解法。
[
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-queens-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
提示:皇后是国际象棋中的棋子,意味着国王的妻子。皇后只做一件事,那就是“吃子”。当她遇见可以吃的棋子时,就迅速冲上去吃掉棋子。当然,她横、竖、斜都可走一到七步,可进可退。(引用自 百度百科 - 皇后 )
N皇后问题是典型的回溯问题,题目的解只能枚举所有的情况,最后从中选择出所有可能的结果。根据题目的描述可知,如果棋盘上的某个格子中已经存在了一个皇后,那么它的同行同列,以及正负对角线上都不应该存在其他的皇后。因此,可以知道结果的数目是固定,可以通过回溯法找到所有可能的结果。
假设某个时刻棋盘中皇后的摆放如下所示:
此时[0, 1]和[1, 3]都已经放上了皇后,且此时的棋盘是不会出现冲突的。考虑第2行的摆放,根据合法性的原则,只能在[2, 0]处放置一个皇后。最后考虑当前棋盘下,最后一行应该如何放置,只能依次进行判断:
[3, 0]:它会与[2, 0]的皇后发生冲突,所以不合法[3, 1]:它会和其他三个已经摆放的皇后都发生冲突,所以同样不合法[3, 2]:它和已放的皇后都不冲突,因此是一个合法位置[3, 3]:根据原则可知,前面已有合法的位置,那么此时后面的位置其实都可以不再进行判断
因此,根据图示的过程我们可以很容易将其归纳到回溯的框架中,判断的顺序可以为:
- 当前行标下所有列的摆放
- 当前位置的正对角线(↗)的摆放
- 当前位置负对角线(↖)的摆放
题目的分析和解题思路和51题是几乎一样的,不同之处在于最后只需要统计合法结果的数量即可,而无需保存可能的的摆放情况。Java解题代码如下:
class Solution {
int count = 0;
public int totalNQueens(int n) {
// 初始化棋盘
char[][] map = new char[n][n];
for (char[] chars : map) {
Arrays.fill(chars, '.');
}
// 以行开始进行判断,当然以列也可以
int row = 0;
// 深度优先搜索
dfs(map, row);
return count;
}
public void dfs(char[][] map, int row){
int length = map.length;
// 终止条件,如果到达最后一行,判断结束
if(row == length){
count += 1;
return;
}
// 选择过程
for (int col = 0; col < length; col++) {
// 判断棋盘上当前位置是否合法
if(!isValid(map, row, col)){
continue;
}
// 如果合法,做出选择,判断下一行可能的摆放情况
map[row][col] = 'Q';
// 回溯
dfs(map, row + 1);
// 撤销选择
map[row][col] = '.';
}
}
private boolean isValid(char[][] map, int row, int col) {
int length = map.length;
// 判断当前行、列的位置是否合法
for (char[] chars : map) {
// 如果其他行的该列上已经放了皇后,则不能再放,说明当前位置不合法
if (chars[col] == 'Q') return false;
}
// 判断右上对角线情况,行减列加
for (int i = row - 1, j = col + 1; i >= 0 && j < length; i--, j++) {
// 如果对角线上已经放了,该位置不合法
if (map[i][j] == 'Q') return false;
}
// 检查左上对角线,行减列减
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
// 如果对角线上已经放了,该位置不合法
if (map[i][j] == 'Q') return false;
}
// 如果该列的所有行和所有对角线上都没有放,则说明为合法位置
return true;
}
}