N皇后-51
sl1673495 opened this issue · comments
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。
提示:
皇后,是国际象棋中的棋子,意味着国王的妻子。皇后只做一件事,那就是“吃子”。当她遇见可以吃的棋子时,就迅速冲上去吃掉棋子。当然,她横、竖、斜都可走一到七步,可进可退。(引用自 百度百科 - 皇后 )
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-queens
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
其实这题虽然是 hard 问题,但是思路比较清晰,还是通过递归不断的根据上一行摆放的结果去找下一行可以摆放的位置,递归进行下去,直到最后一行得出结果。
但是这题的难点在于判断的手法比较复杂,当前一行已经落下一个皇后之后,下一行需要判断三个条件:
- 在这一列上,之前不能摆放过皇后。
- 在对角线 1,也就是「左下 -> 右上」这条对角线上,之前不能摆放过皇后。
- 在对角线 2,也就是「右上 -> 左下」这条对角线上,之前不能摆放过皇后。
难点在于判断对角线上是否摆放过皇后了,其实找到规律后也不难了,看图:
对角线1
:
直接通过这个点的横纵坐标 rowIndex + columnIndex
相加,相等的话就在同在对角线 1 上:
对角线2
:
直接通过这个点的横纵坐标 rowIndex - columnIndex
相减,相等的话就在同在对角线 2 上:
所以:
- 用
columns
数组记录摆放过的列下标,摆放过后直接标记为 true 即可。 - 用
dia1
数组记录摆放过的对角线1下标,摆放过后直接把下标rowIndex + columnIndex
标记为 true 即可。 - 用
dia2
数组记录摆放过的对角线1下标,摆放过后直接把下标rowIndex - columnIndex
标记为 true 即可。 - 递归函数的参数
row
代表每一行中皇后放置的列数,比如row[0] = 3
代表第 0 行皇后放在第 3 列,以此类推。 - 每次进入递归函数前,先把当前项所对应的列、对角线1、对角线2的下标标记为 true,带着标记后的状态进入递归函数。并且在退出本次递归后,需要把这些状态重置为 false ,再进入下一轮循环。
有了这几个辅助知识点,就可以开始编写递归函数了,在每一行,我们都不断的尝试一个坐标点,只要它和之前已有的结果都不冲突,那么就可以放入数组中作为下一次递归的开始值。
这样,如果递归函数顺利的来到了 rowIndex === n
的情况,说明之前的条件全部满足了,一个 n皇后
的解就产生了。
let solveNQueens = function (n) {
let res = []
// 已摆放皇后的的列下标
let columns = []
// 已摆放皇后的对角线1下标 左下 -> 右上
// 计算某个坐标是否在这个对角线的方式是「行下标 + 列下标」是否相等
let dia1 = []
// 已摆放皇后的对角线2下标 左上 -> 右下
// 计算某个坐标是否在这个对角线的方式是「行下标 - 列下标」是否相等
let dia2 = []
// 尝试在一个n皇后问题中 摆放第index行内的皇后位置
let putQueen = (rowIndex, row) => {
if (rowIndex === n) {
res.push(generateBoard(row))
return
}
// 尝试摆第index行的皇后 尝试[0, n-1]列
for (let columnIndex = 0; columnIndex < n; columnIndex++) {
// 在列上不冲突
let columnNotConflict = !columns[columnIndex]
// 在对角线1上不冲突
let dia1NotConflict = !dia1[rowIndex + columnIndex]
// 在对角线2上不冲突
let dia2NotConflict = !dia2[rowIndex - columnIndex]
if (columnNotConflict && dia1NotConflict && dia2NotConflict) {
columns[columnIndex] = true
dia1[rowIndex + columnIndex] = true
dia2[rowIndex - columnIndex] = true
putQueen(rowIndex + 1, row.concat(columnIndex))
columns[columnIndex] = false
dia1[rowIndex + columnIndex] = false
dia2[rowIndex - columnIndex] = false
}
}
}
putQueen(0, [])
return res
}
function generateBoard(row) {
let n = row.length
let res = []
for (let y = 0; y < n; y++) {
let cur = ""
for (let x = 0; x < n; x++) {
if (x === row[y]) {
cur += "Q"
} else {
cur += "."
}
}
res.push(cur)
}
return res
}
var solveNQueens = function (n) {
let row = new Array(n).fill().map(() => false)
let col = new Array(n).fill().map(() => false)
let board = new Array(n).fill().map(() => new Array(n).fill('.'))
let res = []
let helper = function (index, board) {
if (index === n) {
res.push(board.map((arr) => arr.join('')))
return
}
for (let j = 0; j < n; j++) {
if (isCan(index, j, board)) {
board[index][j] = 'Q'
row[index] = true
col[j] = true
helper(index + 1, board)
board[index][j] = '.'
row[index] = false
col[j] = false
}
}
}
helper(0, board)
function isCan(y, x, borad) {
let plus = y + x
let reduce = y - x
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
if (i === y || j === x) continue
if (i + j === plus || i - j === reduce) {
if (borad[i][j] === 'Q') return false
}
}
}
return !row[y] && !col[x]
}
return res
}