解法一:回溯
由于每行恰好放一个皇后,我们知道每行必然有且只有一个皇后,所以如果我们决定在 board[i]
这一行的某一列放置皇后,那么接下来就不用管 board[i]
这一行了,应该考虑 board[i+1]
这一行的皇后要放在哪里。
因此 N皇后问题的穷举对象是每行的皇后放在哪一列,然后通过一些题意约束的冲突规则进行剪枝即可。
func solveNQueens(n int) [][]string {
board := make([][]string, n)
// 初始化空棋盘
for i:=0; i<n; i++{
board[i] = make([]string, n)
for j:=0; j<n; j++{
board[i][j] = "."
}
}
var res [][]string
backtrack(board, 0, &res)
return res
}
func backtrack(board [][]string, row int, res *[][]string){
if row == len(board){ // base case, 所有行都放完
tmp := make([]string, 0)
for i:=0; i<len(board); i++{
tmp = append(tmp, strings.Join(board[i], ""))
}
*res = append(*res, tmp)
return
}
cols := len(board[0])
for col := 0; col < cols; col++{
// 剪枝,跳过非法的位置
if !isValid(board, row, col) {
continue
}
// 选择这一行的这一列放置皇后
board[row][col] = "Q"
// 递归处理下一行
backtrack(board, row+1, res)
// 撤销选择
board[row][col] = "."
}
}
// 因为我们是从上往下放置皇后的,所以只需要检查上方是否有皇后互相冲突
func isValid(board [][]string, row, col int) bool{
// 检查同列的上方是否已放置其它皇后
for i:=row-1; i>=0;i--{
if board[i][col] == "Q"{
return false
}
}
// 检查左、右上方对角线上是否放置其它皇后
for i:=row-1; i>=0; i--{
digCol := col - (row-i)
// 左上方
if digCol >= 0 && board[i][digCol] == "Q"{
return false
}
// 右上方
digCol = col + (row-i)
if digCol < len(board[0]) && board[i][digCol] == "Q"{
return false
}
}
return true
}
遍历左右对角线的for循环可以写成同步更新行和列的for循环,另外由于结果要把一行的所有列合并表示为一个string,我们可以对每一列用rune字符表示
func solveNQueens(n int) [][]string {
board := make([]string, n)
// 初始化空棋盘
for i:=0; i<n; i++{
chars := make([]rune, n)
for j:=0; j<n; j++{
chars[j] = '.'
}
board[i] = string(chars)
}
var res [][]string
backtrack(board, 0, &res)
return res
}
func backtrack(board []string, row int, res *[][]string){
if row == len(board){ // base case, 所有行都放完
tmp := make([]string, len(board))
copy(tmp, board)
*res = append(*res, tmp)
return
}
cols := len(board[0])
for col := 0; col < cols; col++{
// 剪枝,跳过非法的位置
if !isValid(board, row, col) {
continue
}
// 选择这一行的这一列放置皇后
rowChars := []rune(board[row])
rowChars[col] = 'Q'
board[row] = string(rowChars)
// 递归处理下一行
backtrack(board, row+1, res)
// 撤销选择
rowChars[col] = '.'
board[row] = string(rowChars)
}
}
// 因为我们是从上往下放置皇后的,所以只需要检查上方是否有皇后互相冲突
// board一维是[]string,二维是[]rune
func isValid(board []string, row, col int) bool{
// 检查同列的上方是否已放置其它皇后
for i:=row-1; i>=0;i--{
if board[i][col] == 'Q'{
return false
}
}
// 检查左上方对角线上是否放置其它皇后
for i, j := row-1, col-1; i>=0 && j>=0; i,j = i-1, j-1{
if board[i][j] == 'Q'{
return false
}
}
// 检查右上方对角线上是否放置其它皇后
for i, j := row-1, col+1; i>=0 && j<len(board[0]); i,j = i-1, j+1{
if board[i][j] == 'Q'{
return false
}
}
return true
}