Open azl397985856 opened 2 years ago
思路 从0行开始,遍历每一个位置,尝试放置皇后, 通过检查之前存储的皇后们的位置,查看是否在一行,是否在一个对角线上 如果确定一个位置,则继续尝试放置下一个皇后,直到成功放满n个皇后,结果+1
代码
class Solution:
def totalNQueens(self, n: int) -> int:
self.res = 0
self.n = n
self.find_queen()
return self.res
def find_queen(self, row=0, prior=[]):
if row == self.n:
self.res += 1
for i in range(self.n):
if i not in prior: # 和之前的不在一列中
for index, pos in enumerate(prior):
if row-index == abs(i-pos): # 检测之前的位置是否在斜线
break
else:
self.find_queen(row + 1, prior + [i]) # 通过检查,开始寻找下一行的位置
# 这块不要直接更改row和prior的值,传值即可,因为递归失败的话需要回溯,继续用原来的参数检查下一个位置的可行性。
复杂度 时间 O(n!) 空间 O(n)
位运算
var totalNQueens = function(n) {
if(n < 1) return
let count = 0
dfs(n, 0, 0, 0, 0)
return count
function dfs(n, row, cols, diag1, diag2) {
if(row >= n) {
count += 1
return
}
let bits = (~(cols | diag1 | diag2)) & ((1 << n) - 1)
while(bits) {
let p = bits & -bits
bits = bits & (bits - 1)
dfs(n, row + 1, cols | p, (diag1 | p) << 1, (diag2 | p) >>> 1)
}
}
};
思路
代码
class Solution:
def totalNQueens(self, n: int) -> int:
def backtrack(row: int) -> int:
if row == n:
return 1
else:
count = 0
for i in range(n):
if i in columns or row - i in diagonal1 or row + i in diagonal2:
continue
columns.add(i)
diagonal1.add(row - i)
diagonal2.add(row + i)
count += backtrack(row + 1)
columns.remove(i)
diagonal1.remove(row - i)
diagonal2.remove(row + i)
return count
columns = set()
diagonal1 = set()
diagonal2 = set()
return backtrack(0)
复杂度分析
C++ Code:
class Solution {
public:
int totalNQueens(int n) {
// Time O(n^n) Space O(n) Use 15 min.
vector<int> onePath;
int ret=0;
backTrac(n, onePath, 0, ret);
return ret;
}
void backTrac(int n, vector<int>& onePath, int row, int& ret)
{
if(onePath.size()==n) // Find one possible solution.
{
ret++;
return;
}
for(int col =0; col <n; col++) // loop every col and see if it is valid.
{
if(isValid(onePath, row, col))
{
onePath.push_back(col);
backTrac(n, onePath, row+1, ret);
onePath.pop_back();
}
}
}
bool isValid(vector<int>& onePath, int irow, int icol)
{
// vertical
for(int i=0; i< onePath.size(); i++)
{
if(onePath[i] == icol)
return false;
if(onePath[i]+(irow-i) == icol)
return false;
if(onePath[i]-(irow-i) == icol)
return false;
}
return true;
}
};
Backtracking
class Solution {
public:
int totalNQueens(int n) {
set<int> cols, diags, anti_diags;
return backtracking(0, cols, diags, anti_diags, n);
}
int backtracking(int row, set<int> cols, set<int> diags, set<int> anti_diags, int n){
if(row == n) return 1;
int solutions = 0;
for(int col = 0; col<n; col++){
int cur_diag = row-col;
int cur_anti_diag = row+col;
if(cols.find(col) != cols.end() || diags.find(cur_diag) != diags.end()|| anti_diags.find(cur_anti_diag) != anti_diags.end()){
continue;
}
cols.insert(col);
anti_diags.insert(cur_anti_diag);
diags.insert(cur_diag);
solutions += backtracking(row+1,cols, diags, anti_diags, n);
cols.erase(col);
anti_diags.erase(cur_anti_diag);
diags.erase(cur_diag);
}
return solutions;
}
};
Time: O(N!) Space: O(N)
return [1, 0, 0, 2, 10, 4, 40, 92, 352][n - 1]
set
来快速判断当前行列对角线是否有其他棋子。棋子坐标为i
, j
,列集合就用j
,45度对角线集合就是i-j
,135度对角线集合就是i+j
。 def totalNQueens(self, n: int) -> int:
col=set()
diagonal_45=set()
diagonal_135=set()
ans=0
def dfs(dep,i):
nonlocal ans
if dep==0:
ans+=1
return
for j in range(n):
if j not in col and i-j not in diagonal_45 and i+j not in diagonal_135:
col.add(j)
diagonal_45.add(i-j)
diagonal_135.add(i+j)
dfs(dep-1,i+1)
col.remove(j)
diagonal_45.remove(i-j)
diagonal_135.remove(i+j)
dfs(n,0)
return ans
class Solution {
public int res = 0;
Set<Integer> col = new HashSet<>();
Set<Integer> diff = new HashSet<>();
Set<Integer> sum = new HashSet<>();
public int totalNQueens(int n) {
if(n <= 0) return res;
dfs(0, n);
return res;
}
private void dfs(int level, int n) {
if(level == n) {
res++;
return;
}
//row not equal
for(int i = 0; i < n; i++) {
if(!col.contains(i) && !diff.contains(level - i) && !sum.contains(level + i)) {
col.add(i);
diff.add(level - i);
sum.add(level + i);
dfs(level + 1, n);
col.remove(i);
diff.remove(level - i);
sum.remove(level + i);
}
}
}
}
复杂度分析
Code:
public class Solution {
public int res;
public int TotalNQueens(int n) {
res = 0;
List<int> boardCols = new List<int>();
DFSHelper(boardCols, n);
return res;
}
public void DFSHelper(List<int> boardCols, int n)
{
if (boardCols.Count == n)
{
res++;
return;
}
for(int col = 0; col < n; col++ )
{
if (!IsValid(boardCols, col))
continue;
boardCols.Add(col);
DFSHelper(boardCols, n);
boardCols.RemoveAt(boardCols.Count - 1);
}
}
public bool IsValid(List<int> boardCols, int colIndex)
{
int row = boardCols.Count;
for (int rowIndex = 0; rowIndex < row; rowIndex++)
{
if (boardCols[rowIndex] == colIndex)
return false;
if (rowIndex + boardCols[rowIndex] == row + colIndex)
return false;
if (rowIndex - boardCols[rowIndex] == row - colIndex)
return false;
}
return true;
}
}
思路: 回溯+位运算
func totalNQueens(n int) int {
out := 0
var dfs func(row int,col int,diag1 int,diag2 int)
dfs = func(row int,col int,diag1 int,diag2 int) {
if row == n{
out++
return
}
possible := (1<<n-1)&^(col|diag1|diag2)
for possible>0{
choose := possible&(-possible)
dfs(row+1,choose|col,(choose|diag1)<<1,(choose|diag2)>>1)
possible &= (possible-1)
}
}
dfs(0,0,0,0)
return out
}
class Solution {
public List<List<String>> solveNQueens(int n) {
List<List<String>> solutions = new ArrayList<List<String>>();
int[] queens = new int[n];
Arrays.fill(queens, -1);
Set<Integer> columns = new HashSet<Integer>();
Set<Integer> diagonals1 = new HashSet<Integer>();
Set<Integer> diagonals2 = new HashSet<Integer>();
backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
return solutions;
}
public void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
if (row == n) {
List<String> board = generateBoard(queens, n);
solutions.add(board);
} else {
for (int i = 0; i < n; i++) {
if (columns.contains(i)) {
continue;
}
int diagonal1 = row - i;
if (diagonals1.contains(diagonal1)) {
continue;
}
int diagonal2 = row + i;
if (diagonals2.contains(diagonal2)) {
continue;
}
queens[row] = i;
columns.add(i);
diagonals1.add(diagonal1);
diagonals2.add(diagonal2);
backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);
queens[row] = -1;
columns.remove(i);
diagonals1.remove(diagonal1);
diagonals2.remove(diagonal2);
}
}
}
public List<String> generateBoard(int[] queens, int n) {
List<String> board = new ArrayList<String>();
for (int i = 0; i < n; i++) {
char[] row = new char[n];
Arrays.fill(row, '.');
row[queens[i]] = 'Q';
board.add(new String(row));
}
return board;
}
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。
位运算 + dfs
两个位运算操作
1.x & -x。
这个操作可以保留最后一位1,而其他位都会清零。比如,一个8位数,00110110.进行了这个操作后就会变为00000010.我在这里想了很久,也没明白原因,后来发现原来是之前学的东西都还给了老师。。。这里的关键是:计算机存储数的时候存储的是补码,正数的补码是其本身,而负数的补码是其反码加1.因此,00110110加一个负号以后就变成了10110110(姑且认为最高位是符号位),其反码为11001001,补码为11001010。这个跟原来的数按位与后就是00000010
2.x & (x - 1)。将最后一位1变为0。可以先这么考虑一下,最低位如果是1,减1后就变为了0,按位与后,其他位不发生变化,最低位清为0.现在考虑更复杂的情况,如果最低位是0,那么减1后,这个0就变为了1,并且向高位借位,直到遇到第一个1,这个1会因为之前的借位变成0,此时借位清0,更高位的数据就不会发生变化。这样按位与后,最低位的1就变成了0,而其他的数并没有变化。
class Solution {
int res=0;
public int totalNQueens(int n) {
dfs(n,0,0,0,0);//位中0表示能放皇后
return res;
}
//n表示多少个皇后,row表示放第几行的皇后,col中位为1表示不能放皇后,ld和rd同理
public void dfs(int n,int row,int col,int ld,int rd){
if(row==n){
res++;
return;
}
//col|ld|rd找出不可以放置皇后的位置(1表示),取反之后表示1代表能放皇后的位置
//(1<<n)-1先把1变成1后面n个0,减1之后就是低位存在n个连续的1,即能放皇后
//这里一开始col,ld,rd都是0,但实际上是n位的0
int canQueens=~(col|ld|rd)&((1<<n)-1);
//没有放完全部皇后的位置
while(canQueens>0){
int place=canQueens&(-canQueens);//取最低位的1,表示选中皇后的位置,比如010110变成了010010
dfs(n,row+1,col|place,(ld|place)<<1,(rd|place)>>1);
//ROW+1表示下一行,col|place表示目前已经放置了皇后的列(1表示),后面的皇后不能放这些列1
//ld表示到第row行时由于对角线(左下)产生的不能放皇后的位置,
//(ld|place)<<1表示当前已放置皇后到第row+1行所产生的不能放皇后的位置
//rd即(右下)同理
canQueens&=(canQueens-1);//当前行的皇后少了一个选择(canQueens中最后那个1)
}
}
}
时间复杂度:$O(n!)$
空间复杂度:$O(n)$
class Solution:
def totalNQueens(self, n: int) -> int:
def backtrack(row: int) -> int:
if row == n:
return 1
else:
count = 0
for i in range(n):
if i in columns or row - i in diagonal1 or row + i in diagonal2:
continue
columns.add(i)
diagonal1.add(row - i)
diagonal2.add(row + i)
count += backtrack(row + 1)
columns.remove(i)
diagonal1.remove(row - i)
diagonal2.remove(row + i)
return count
columns = set()
diagonal1 = set()
diagonal2 = set()
return backtrack(0)
思路: 回溯+位运算
func totalNQueens(n int) int { out := 0 var dfs func(row int,col int,diag1 int,diag2 int) dfs = func(row int,col int,diag1 int,diag2 int) { if row == n{ out++ return } possible := (1<<n-1)&^(col|diag1|diag2) for possible>0{ choose := possible&(-possible) dfs(row+1,choose|col,(choose|diag1)<<1,(choose|diag2)>>1) possible &= (possible-1) } } dfs(0,0,0,0) return out }
回溯。
不能攻击的定义是:在同一行,同一列,同一斜线上只有一个棋子。
假设我们一个一个的摆棋子,当第一行摆上一个棋子之后,这一行就再不能放棋子了。 那么我们就可以将问题变为,在每一行放一个棋子,看把它放在哪个列可行。
CPP
class Solution {
public:
int totalNQueens(int n) {
unordered_set<int> col, diag1, diag2;
return backtrack(n, 0, col, diag1, diag2);
}
private:
int backtrack(int n, int row, unordered_set<int>& col, unordered_set<int>& diag1, unordered_set<int>& diag2)
/*
* @n: number of the queens, also the size of the board
* @row: current row number
* @col: a set that stores already used columns
* @diag1: a set that stores positions that are used in the left to right diagonal
* @diag2: right to left diagonal
*/
{
if (row == n) { // the last row, all other columns are used
return 1;
}
int count = 0;
for (int i = 0; i < n; i++) {
if (col.find(i) != col.end()) {
// this column is used
continue;
}
// left to right: row_idx - col_idx are the same
int diagonal1 = row - i;
if (diag1.find(diagonal1) != diag1.end()) {
continue;
}
// right to left: row_idx + col_idx are the same
int diagonal2 = row + i;
if (diag2.find(diagonal2) != diag2.end()) {
continue;
}
// mark this position
col.insert(i);
diag1.insert(diagonal1);
diag2.insert(diagonal2);
count += backtrack(n, row+1, col, diag1, diag2);
// states under this root are all checked, unmark
col.erase(i);
diag1.erase(diagonal1);
diag2.erase(diagonal2);
}
return count;
}
};
复杂度分析
思路:回溯 ··· func totalNQueens(n int) int { count := 0 // 当n=2,3时,n皇后是无解的 if n > 1 && n <= 3 { return count } // n=1时,有唯一解 if n == 1 { return 1 }
//初始化
board := make([][]string, n)
for i := 0; i < n; i++ {
board[i] = make([]string, n)
}
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
board[i][j] = "."
}
}
var backTrack func(int, [][]string)
backTrack = func(row int, board [][]string) {
// 递归终止条件,如果递归到最后一行,则得到一个解
if row == n {
count++
return
}
for column := 0; column < n; column++ {
// 如果不满足皇后们的约束条件,则跳过本次循环
if !IsValid(row, column, n, board) {
continue
}
// 如果满足约束条件, 则在该位置放置一个皇后
board[row][column] = "Q"
// 递归
backTrack(row+1, board)
// 回溯
board[row][column] = "."
}
}
backTrack(0, board)
return count
}
// IsValid 判断在board[row][column]位置放置皇后是否满足所有约束条件 func IsValid(row, column, n int, board [][]string) bool { // 由于单层搜索时,每一层递归都只会选同一行里的一个元素,所以行不用去重了。 // 检查列 for i := 0; i < row; i++ { if board[i][column] == "Q" { return false } } // 检查45度对角线 for i, j := row-1, column-1; i >= 0 && j >= 0; i, j = i-1, j-1 { if board[i][j] == "Q" { return false } } // 检查135度对角线 for i, j := row-1, column+1; i >= 0 && j < n; i, j = i-1, j+1 { if board[i][j] == "Q" { return false } } return true } ···
思路: 回溯 func totalNQueens(n int) int { out := 0 var dfs func(row int,col int,diag1 int,diag2 int) dfs = func(row int,col int,diag1 int,diag2 int) { if row == n{ out++ return } possible := (1<<n-1)&^(col|diag1|diag2) for possible>0{ choose := possible&(-possible) dfs(row+1,choose|col,(choose|diag1)<<1,(choose|diag2)>>1) possible &= (possible-1) } } dfs(0,0,0,0) return out } 时间复杂度:O(N) 空间复杂度:O(N)
class Solution:
def totalNQueens(self, n):
def backtrack(row, diagonals, anti_diagonals, cols):
# Base case - N queens have been placed
if row == n:
return 1
solutions = 0
for col in range(n):
curr_diagonal = row - col
curr_anti_diagonal = row + col
# If the queen is not placeable
if (col in cols
or curr_diagonal in diagonals
or curr_anti_diagonal in anti_diagonals):
continue
# "Add" the queen to the board
cols.add(col)
diagonals.add(curr_diagonal)
anti_diagonals.add(curr_anti_diagonal)
# Move on to the next row with the updated board state
solutions += backtrack(row + 1, diagonals, anti_diagonals, cols)
# "Remove" the queen from the board since we have already
# explored all valid paths using the above function call
cols.remove(col)
diagonals.remove(curr_diagonal)
anti_diagonals.remove(curr_anti_diagonal)
return solutions
return backtrack(0, set(), set(), set())
from collections import defaultdict
class Solution:
def totalNQueens(self, n: int) -> int:
global dicy ,dic1,dic2,res
dicy = defaultdict(int)
dic1 = defaultdict(int)
dic2 = defaultdict(int)
def backtrack(x):
global dicy ,dic1,dic2 , res
if x == n:
res += 1
for j in range(n):
if dicy[j] != 0 or dic1[x+j] != 0 or dic2[x - j ] != 0:
continue
dicy[j] , dic1[x+j] , dic2[x - j] = 1 , 1 , 1
backtrack(x + 1)
dicy[j] , dic1[x+j] , dic2[x - j] = 0 , 0 , 0
res = 0
backtrack(0)
return res
class Solution:
def totalNQueens(self, n: int) -> int:
self.res = 0
#flag for column (n), 2 sets of diagonals (2n-1) * 2
self.flag = [0] * (5*n - 2)
self.backtrack(0, n)
return self.res
def backtrack(self, row, n):
if row == n:
self.res += 1
return
for col in range(n):
if self.flag[col] or self.flag[2*n+col-row-1] or self.flag[3*n+row+col-1]:
continue
self.flag[col] = 1
self.flag[2*n+col-row-1] = 1
self.flag[3*n+row+col-1] = 1
self.backtrack(row+1, n)
self.flag[col] = 0
self.flag[2*n+col-row-1] = 0
self.flag[3*n+row+col-1] = 0
TC: O(N!) SC: O(N)
class Solution:
def totalNQueens(self, n):
def backtrack(row, diagonals, anti_diagonals, cols):
# Base case - N queens have been placed
if row == n:
return 1
solutions = 0
for col in range(n):
curr_diagonal = row - col
curr_anti_diagonal = row + col
# If the queen is not placeable
if (col in cols
or curr_diagonal in diagonals
or curr_anti_diagonal in anti_diagonals):
continue
# "Add" the queen to the board
cols.add(col)
diagonals.add(curr_diagonal)
anti_diagonals.add(curr_anti_diagonal)
# Move on to the next row with the updated board state
solutions += backtrack(row + 1, diagonals, anti_diagonals, cols)
# "Remove" the queen from the board since we have already
# explored all valid paths using the above function call
cols.remove(col)
diagonals.remove(curr_diagonal)
anti_diagonals.remove(curr_anti_diagonal)
return solutions
return backtrack(0, set(), set(), set())
回溯
/**
* @param {number} n
* @return {number}
*/
var totalNQueens = function (n) {
const board = new Array(n).fill('.').map(() => new Array(n).fill('.'));
let res = 0;
backtrack(0);
return res;
function backtrack(row) {
if (row === n) {
res++;
return;
}
for (let col = 0; col < n; col++) {
if (!isValid(row, col)) {
continue;
}
board[row][col] = 'Q';
backtrack(row + 1);
board[row][col] = '.';
}
}
function isValid(row, col) {
// 因为是从上往下一行一行放的,所以不需要下方的行
for (let i = 0; i < row; i++) {
for (let j = 0; j < n; j++) {
// 如果发现了 Q,并且和自己同列或同对角线
// 判断同对角线,使用 |y1 - y2| === |x1 - x2|
if (board[i][j] === 'Q') {
if (j === col || Math.abs(i - row) === Math.abs(j - col)) {
return false;
}
}
}
}
return true;
}
};
时间:O(N!) 空间:O(N^2)
回溯: 就是比暴力还暴力的解法, 各种尝试,不行就返回 本题最主要的就是怎么才能找到每个位置的对角线
因为每一行和每一列只能放置一个皇后,在第一行放置完第一个皇后以后,要挪到下一行去放置第二个皇后
class Solution:
def totalNQueens(self, n: int) -> int:
columns = set()
posDiag = set() # (r + c) # 上升对角线 /
negDiag = set() # (r - c) # 下降对角线 \
def backtrack(row):
if row == n:
return 1
else:
count = 0
for col in range(n):
if col in columns or (row + col) in posDiag or (row - col) in negDiag:
continue
columns.add(col)
posDiag.add(row + col)
negDiag.add(row - col)
count += backtrack(row + 1) # 51题, board[col][row] = 'Q'
columns.remove(col)
posDiag.remove(row + col)
negDiag.remove(row - col)
return count
return backtrack(0)
var totalNQueens = function(n) {
let res = 0;
function dfs(row, col, dlr, dll) {
if (row === n) return res++;
for (let i = 0; i < n; i++) {
const _col = 1 << i, _dlr = 1 << (i - row + n), _dll = 1 << (i + row);
if ((col & _col) !== 0 || (dlr & _dlr) !== 0 || (dll & _dll) !== 0) continue;
dfs(row + 1, col | _col, dlr | _dlr, dll | _dll);
}
}
dfs(0, 0, 0, 0)
return res;
};
Thoughts Using three sets to check if any queen exist in the column, and two diagonals. Need to turn the status back after backtrack
Code
class Solution {
public int totalNQueens(int n) {
Set<Integer> columns = new HashSet<>();
Set<Integer> diagonals1 = new HashSet<>();
Set<Integer> diagonals2 = new HashSet<>();
return backtrack(n, 0, columns, diagonals1, diagonals2);
}
private int backtrack(int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
if (row == n) {
return 1;
} else {
int count = 0;
for (int i = 0; i < n; i++) {
if (columns.contains(i)) {
continue;
}
int diagonal1 = row - i;
if (diagonals1.contains(diagonal1)) {
continue;
}
int diagonal2 = row + i;
if (diagonals2.contains(diagonal2)) {
continue;
}
columns.add(i);
diagonals1.add(diagonal1);
diagonals2.add(diagonal2);
count += backtrack(n, row + 1, columns, diagonals1, diagonals2);
columns.remove(i);
diagonals1.remove(diagonal1);
diagonals2.remove(diagonal2);
}
return count;
}
}
}
Complexity Time: O(N!) Space: O(n)
class Solution:
def totalNQueens(self, n):
def backtrack(row, diagonals, anti_diagonals, cols):
# Base case - N queens have been placed
if row == n:
return 1
solutions = 0
for col in range(n):
curr_diagonal = row - col
curr_anti_diagonal = row + col
# If the queen is not placeable
if (col in cols
or curr_diagonal in diagonals
or curr_anti_diagonal in anti_diagonals):
continue
# "Add" the queen to the board
cols.add(col)
diagonals.add(curr_diagonal)
anti_diagonals.add(curr_anti_diagonal)
# Move on to the next row with the updated board state
solutions += backtrack(row + 1, diagonals, anti_diagonals, cols)
# "Remove" the queen from the board since we have already
# explored all valid paths using the above function call
cols.remove(col)
diagonals.remove(curr_diagonal)
anti_diagonals.remove(curr_anti_diagonal)
return solutions
return backtrack(0, set(), set(), set())
backtracking
class Solution {
int size = 0;
public int totalNQueens(int n) {
size = n;
return backtrack(0, new HashSet<Integer>(), new HashSet<Integer>(), new HashSet<Integer>());
}
public int backtrack(int row, HashSet<Integer> col, HashSet<Integer> diagnol, HashSet<Integer> antidiagnol){
if(row >= size){
return 1;
}
int ans = 0;
for(int i = 0; i < size; i++){
//check (row, i)
if(!col.contains(i) && !diagnol.contains(row - i)
&& !antidiagnol.contains(i + row)){
// System.out.println("1");
col.add(i);
diagnol.add(row - i);
antidiagnol.add(row + i);
ans += backtrack(row + 1, col, diagnol, antidiagnol);
col.remove(i);
diagnol.remove(row - i);
antidiagnol.remove(row + i);
}
}
return ans;
}
}
复杂度分析
class Solution:
def totalNQueens(self, n: int) -> int:
self.count = 0
self.n = n
self.col_set = set()
self.diag_set_1 = set()
self.diag_set_2 = set()
self.dfs(0)
return self.count
def dfs(self,row):
if row == self.n:
self.count += 1
return
for col in range(self.n):
if col not in self.col_set and (col - row) not in self.diag_set_1 and (col + row) not in self.diag_set_2:
self.col_set.add(col)
self.diag_set_1.add(col - row)
self.diag_set_2.add(col + row)
self.dfs(row+1)
self.col_set.remove(col)
self.diag_set_1.remove(col - row)
self.diag_set_2.remove(col + row)
time complexity: O(N!) space complexity: O(N)
backtracking
顺便做51
class Solution {
List<List<Integer>> res = new ArrayList<>();
public int totalNQueens(int n) {
char[][] chessboard = new char[n][n];
for(char[] c : chessboard){
Arrays.fill(c, '.');
}
backtrack(0, chessboard, n);
return res.size();
}
void backtrack(int row, char[][] chessboard, int n){
if(row == n){
res.add(Array2List(chessboard));
return;
}
for(int col = 0; col < n; col++){
if(isValid(row, col, chessboard, n)){
chessboard[row][col] = 'Q';
backtrack(row + 1, chessboard, n);
chessboard[row][col] = '.';
}
}
}
List Array2List(char[][] chessboard){
List<String> list = new ArrayList<>();
for(char[] c : chessboard){
list.add(String.copyValueOf(c));
}
return list;
}
boolean isValid(int row, int col, char[][] chessboard, int n){
for(int i = 0; i < row; i++){
if(chessboard[i][col] == 'Q'){
return false;
}
}
for(int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--){
if(chessboard[i][j] == 'Q'){
return false;
}
}
for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++){
if(chessboard[i][j] == 'Q'){
return false;
}
}
return true;
}
}
复杂度分析
var solveNQueens = function (n) {
function isValid(row, col, chessBoard, n) {
for (let i = 0; i < row; i++) {
if (chessBoard[i][col] === 'Q') {
return false;
}
}
for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (chessBoard[i][j] === 'Q') {
return false;
}
}
for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (chessBoard[i][j] === 'Q') {
return false;
}
}
return true;
}
function transformChessBoard(chessBoard) {
let chessBoardBack = [];
chessBoard.forEach((row) => {
let rowStr = '';
row.forEach((value) => {
rowStr += value;
});
chessBoardBack.push(rowStr);
});
return chessBoardBack;
}
let result = [];
function backtracing(row, chessBoard) {
if (row === n) {
result.push(transformChessBoard(chessBoard));
return;
}
for (let col = 0; col < n; col++) {
if (isValid(row, col, chessBoard, n)) {
chessBoard[row][col] = 'Q';
backtracing(row + 1, chessBoard);
chessBoard[row][col] = '.';
}
}
}
let chessBoard = new Array(n).fill([]).map(() => new Array(n).fill('.'));
backtracing(0, chessBoard);
return result;
};
回溯
class Solution {
int ans = 0;
public int totalNQueens(int n) {
Set<Integer> col = new HashSet<>();
Set<Integer> r = new HashSet<>();
Set<Integer> l = new HashSet<>();
backtrack(n, 0, col, r, l);
return ans;
}
private void backtrack(int n, int row, Set<Integer> col, Set<Integer> r, Set<Integer> l) {
if (row == n) {
++ans;
return;
}
for (int i = 0; i < n; ++i) {
if (col.contains(i)) {
continue;
}
if (r.contains(row - i)) {
continue;
}
if (l.contains(row + i)) {
continue;
}
col.add(i);
r.add(row - i);
l.add(row + i);
backtrack(n, row + 1, col, r, l);
col.remove(i);
r.remove(row - i);
l.remove(row + i);
}
}
}
//cpp 1-29
class Solution {
public:
int ret = 0;;
void backtrack(int n, int row, vector<string>& cb) {
if (row == n) {
ret++;
return;
}
for (int col = 0; col < n; col++) {
if (judge(row, col, cb, n)) {
cb[row][col] = 'Q';
backtrack(n, row+1, cb);
cb[row][col] = '.'; // 回溯
}
}
}
bool judge (int row, int col, vector<string> & cb, int n) {
// 每行判断
for (int i = 0; i < row; i++) {
if (cb[i][col] == 'Q') {
return false;
}
}
// 左上角
for (int i = row-1, j = col-1; i >= 0&&j >= 0; i--, j--) {
if(cb[i][j] == 'Q') {
return false;
}
}
// 右上角
for (int i = row-1, j = col+1; i >= 0&&j < n; i--, j++) {
if(cb[i][j] == 'Q') {
return false;
}
}
return true;
}
int totalNQueens(int n) {
vector<string> cb(n, string(n, '.'));
backtrack(n, 0 , cb);
return ret;
}
};
n皇后是回溯法非常经典的题了
这题要的是解法数量,如果要解法的话,把count改成string就行,或者一个int数组,只要能存储第i个皇后放置的位置的数据结构都可以
class Solution {
public:
bool isvalid(vector<vector<bool>>& board,int row,int col,int n){
for(int i = 0;i<row;i++){
if(board[i][col]) return false;
}
for(int i = row-1;i>=0;i--){
if(col-row+i<0) break;
if(board[i][col-row+i]) return false;
}
for(int i = row-1;i>=0;i--){
if(col+row-i>=n) break;
if(board[i][col+row-i]) return false;
}
return true;
}
void backtrack(vector<vector<bool>>& board,int row,int n,int& count){
if(row==n){
count++;
return;
}
for(int col = 0;col<n;col++){
if(isvalid(board,row,col,n)){
board[row][col] = true;
backtrack(board,row+1,n,count);
board[row][col] = false;
}
}
}
int totalNQueens(int n) {
int count = 0;
vector<vector<bool>> board(n,vector<bool>(n,false));
backtrack(board,0,n,count);
return count;
}
};
复杂度分析
时间复杂度:O(n!)
空间复杂度:O(n)
class Solution:
def totalNQueens(self, n: int) -> int:
ans = 0
def backtrack(row, diagonals, anti_diagonals, cols):
nonlocal ans
if row == n:
ans += 1
return
for col in range(n):
cur_diagonal = row + col
cur_anti_diagonal = row - col
if col in cols or cur_diagonal in diagonals or cur_anti_diagonal in anti_diagonals or col in cols:
continue
diagonals.add(cur_diagonal)
anti_diagonals.add(cur_anti_diagonal)
cols.add(col)
backtrack(row + 1, diagonals, anti_diagonals, cols)
diagonals.remove(cur_diagonal)
anti_diagonals.remove(cur_anti_diagonal)
cols.remove(col)
backtrack(0, set(), set(), set())
return ans
Time complexity O(n!) Space complexity O(n)
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
def create_board(state):
board = []
for row in state:
board.append(''.join(row))
return board
def backtrack(row, cols, diagonals, anti_diagonals, state):
if row == n:
ans.append(create_board(state))
return
for col in range(n):
d = row - col
anti_d = row + col
if col in cols or d in diagonals or anti_d in anti_diagonals:
continue
state[row][col] = 'Q'
diagonals.add(d)
anti_diagonals.add(anti_d)
cols.add(col)
backtrack(row + 1, cols, diagonals, anti_diagonals,state)
state[row][col] = '.'
diagonals.remove(d)
anti_diagonals.remove(anti_d)
cols.remove(col)
empty_board = [['.'] * n for _ in range(n)]
ans = []
backtrack(0, set(), set(), set(), empty_board)
return ans
思路:
回溯法(Backtracking)
复杂度分析:
方法一、
class Solution {
public:
int totalNQueens(int n) {
set
return backTracking(n, 0, rows, d1, d2);
}
private:
int backTracking(int n, int col, set
int res = 0;
for (int i = 0; i < n; ++i) {
if (rows.count(i) || d1.count(col - i) || d2.count(col + i)) continue;
rows.insert(i);
d1.insert(col - i);
d2.insert(col + i);
res += backTrack(n, col + 1, rows, d1, d2);
rows.erase(i);
d1.erase(col - i);
d2.erase(col + i);
}
return res;
}
};
好难,先抄作业
const totalNQueens = function (n) {
let res = 0;
const dfs = (n, row, col, pie, na) => {
if (row >= n) {
res++;
return;
}
// 将所有能放置 Q 的位置由 0 变成 1,以便进行后续的位遍历
// 也就是得到当前所有的空位
let bits = ~(col | pie | na) & ((1 << n) - 1);
while (bits) {
// 取最低位的1
let p = bits & -bits;
// 把P位置上放入皇后
bits = bits & (bits - 1);
// row + 1 搜索下一行可能的位置
// col | p 目前所有放置皇后的列
// (pie | p) << 1 和 (na | p) >> 1) 与已放置过皇后的位置 位于一条斜线上的位置
dfs(n, row + 1, col | p, (pie | p) << 1, (na | p) >> 1);
}
};
dfs(n, 0, 0, 0, 0);
return res;
};
思路
皇后的同一行、同一列、左右方向的斜线上都不能有皇后,考虑用位运算分别表示已经占据的列、左斜线、右斜线的信息,1表示已经占据,0表示可以使用。
代码
var totalNQueens = function (n) {
let res = 0;
dfs(n, 0, 0, 0, 0);
return res;
function dfs(n, row, cols, diag1, diag2) {
if (n === row) res++;
// 此时1表明下一轮可取的范围
let bits = (~(cols | diag1 | diag2)) & ((1 << n) - 1);
while (bits) {
// 取最右一位1
let p = bits & (-bits);
// 最右1位1归0
bits = bits & (bits - 1);
// <<表示向左下方 >>表示向右下方移动
dfs(n, row + 1, (cols | p), (diag1 | p) << 1, (diag2 | p) >> 1);
}
}
};
复杂度分析
class Solution(object):
def totalNQueens(self, n):
"""
:type n: int
:rtype: int
"""
def backtrack(row):
if row == n:
return 1
else:
count = 0
for i in range(n):
if i in cols or row - i in dia1 or row + i in dia2:
continue
cols.add(i)
dia1.add(row - i)
dia2.add(row + i)
count += backtrack(row + 1)
cols.remove(i)
dia1.remove(row - i)
dia2.remove(row + i)
return count
cols, dia1, dia2 = set(), set(), set()
return backtrack(0)
不会。。。看答案 回溯?
class Solution {
public int totalNQueens(int n) {
Set<Integer> columns = new HashSet<Integer>();
Set<Integer> diagonals1 = new HashSet<Integer>();
Set<Integer> diagonals2 = new HashSet<Integer>();
return backtrack(n, 0, columns, diagonals1, diagonals2);
}
public int backtrack(int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
if (row == n) {
return 1;
} else {
int count = 0;
for (int i = 0; i < n; i++) {
if (columns.contains(i)) {
continue;
}
int diagonal1 = row - i;
if (diagonals1.contains(diagonal1)) {
continue;
}
int diagonal2 = row + i;
if (diagonals2.contains(diagonal2)) {
continue;
}
columns.add(i);
diagonals1.add(diagonal1);
diagonals2.add(diagonal2);
count += backtrack(n, row + 1, columns, diagonals1, diagonals2);
columns.remove(i);
diagonals1.remove(diagonal1);
diagonals2.remove(diagonal2);
}
return count;
}
}
}
Time O(N) Space O(N)
回溯
var deal = (n, row, col, arr1, arr2) => {
if (row === n) return 1;
let count = 0;
for (let i = 0; i < n; i++) {
if (col.has(i)) continue;
if (arr1.has(row - i)) continue;
if (arr2.has(row + i)) continue;
col.add(i);
arr1.add(row - i);
arr2.add(row + i);
count += deal(n, row + 1, col, arr1, arr2);
col.delete(i);
arr1.delete(row - i);
arr2.delete(row + i);
}
return count;
}
var totalNQueens = function(n) {
const col = new Set();
const arr1 = new Set();
const arr2 = new Set();
return deal(n, 0, col, arr1, arr2);
};
时间复杂度:O(n!)
空间复杂度:O(n)
思路 DFS(深度优先搜索)
const totalNQueens = function (n) { let res = 0; const dfs = (n, row, col, pie, na) => { if (row >= n) { res++; return; } // 将所有能放置 Q 的位置由 0 变成 1,以便进行后续的位遍历 // 也就是得到当前所有的空位 let bits = ~(col | pie | na) & ((1 << n) - 1); while (bits) { // 取最低位的1 let p = bits & -bits; // 把P位置上放入皇后 bits = bits & (bits - 1); // row + 1 搜索下一行可能的位置 // col | p 目前所有放置皇后的列 // (pie | p) << 1 和 (na | p) >> 1) 与已放置过皇后的位置 位于一条斜线上的位置 dfs(n, row + 1, col | p, (pie | p) << 1, (na | p) >> 1); } }; dfs(n, 0, 0, 0, 0); return res; };
时间复杂度为 O(n!) 空间复杂度为 O(n)
回溯算法,通过公式将每一个皇后的位置摆放好,不断计数,达到n的话表明该方法成立 ans+1
func totalNQueens(n int) (ans int) {
columns := make([]bool, n)
diagonals1 := make([]bool, 2*n-1)
diagonals2 := make([]bool, 2*n-1)
var backtrack func(int)
backtrack = func(row int) {
if row == n{
ans++
return
}
for col, hasQueen := range columns{
d1, d2 :=row+n-1-col, row+col
if hasQueen || diagonals1[d1] || diagonals2[d2]{
continue
}
columns[col] = true
diagonals1[d1] = true
diagonals2[d2] = true
backtrack(row+1)
columns[col] = false
}
}
backtrack(0)
return
}
时间:O(n!) 空间:O(n)
class Solution {
public int totalNQueens(int n) {
Set<Integer> columns = new HashSet<Integer>();
Set<Integer> diagonals1 = new HashSet<Integer>();
Set<Integer> diagonals2 = new HashSet<Integer>();
return backtrack(n, 0, columns, diagonals1, diagonals2);
}
public int backtrack(int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
if (row == n) {
return 1;
} else {
int count = 0;
for (int i = 0; i < n; i++) {
if (columns.contains(i)) {
continue;
}
int diagonal1 = row - i;
if (diagonals1.contains(diagonal1)) {
continue;
}
int diagonal2 = row + i;
if (diagonals2.contains(diagonal2)) {
continue;
}
columns.add(i);
diagonals1.add(diagonal1);
diagonals2.add(diagonal2);
count += backtrack(n, row + 1, columns, diagonals1, diagonals2);
columns.remove(i);
diagonals1.remove(diagonal1);
diagonals2.remove(diagonal2);
}
return count;
}
}
}
回溯法
时间复杂度 O(n!) 空间复杂度 O(n)
class Solution:
def totalNQueens(self, n: int) -> int:
p = [-1] * n
def check(i: int, j: int) -> bool:
for x in range(i):
if p[x] == j: return False
if p[x] == j + i - x or p[x] == j - i + x: return False
return True
ans = 0
def dfs(i: int):
if i == n:
nonlocal ans
ans += 1
else:
for j in range(n):
if check(i, j):
p[i] = j
dfs(i + 1)
p[i] = -1
dfs(0)
return ans
++
n 皇后问题 研究的是如何将 n
个皇后放置在 n × n
的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n
,返回 n 皇后问题 不同的解决方案的数量。
class Solution {
public:
int totalNQueens(int n) {
dfs(n, 0, 0, 0, 0);
return res;
}
void dfs(int n, int row, int col, int ld, int rd){
if(row >= n){
res++;
return;
}
int bits = ~(col | ld | rd) & ((1 << n) - 1); // bits 代表了当前行可放置的col, ((1 << n) - 1) 代表设置了n个1,col | ld | rd分别代表由于同一列,左斜,右斜而无法使用的列
while(bits > 0){
int pick = bits & (-bits); // 提取bits的最后一个1
dfs(n, row + 1, col | pick, (ld | pick) << 1, (rd | pick) >> 1);
bits = bits & bits - 1; // bits的最后一个1替换为0
}
}
private:
int res = 0;
};
思路
class Solution {
public int totalNQueens(int n) {
Set<Integer> columns = new HashSet();
Set<Integer> diagonals1 = new HashSet();
Set<Integer> diagonals2 = new HashSet();
return backtrack(n,0,columns,diagonals1,diagonals2);
}
public int backtrack(int n,int row,Set<Integer> columns,Set<Integer> diagonals1,Set<Integer> diagonals2 ){
if(row == n){
return 1;
}
int count = 0;
for(int i = 0;i<n;i++){
if(columns.contains(i)){
continue;
}
if(diagonals1.contains(row+i)){
continue;
}
if(diagonals2.contains(row-i)){
continue;
}
count+=backtrack(n,row+1,columns,diagonals1,diagonals2);
columns.remove(i);
diagonals1.remove(row+i);
diagonals2.remove(row-i);
}
return count;
}
}
时间:O(n!),回溯
空间:O(n),调用栈不超过n,集合长度不会超过n
class Solution: def totalNQueens(self, n: int) -> int: def backtrack(row: int) -> int: if row == n: return 1 else: count = 0 for i in range(n): if i in columns or row - i in diagonal1 or row + i in diagonal2: continue columns.add(i) diagonal1.add(row - i) diagonal2.add(row + i) count += backtrack(row + 1) columns.remove(i) diagonal1.remove(row - i) diagonal2.remove(row + i) return count
columns = set()
diagonal1 = set()
diagonal2 = set()
return backtrack(0)
/**
* @param {number} n
* @return {number}
* @param row 当前层
* @param col 列
* @param pie 左斜线
* @param na 右斜线
*/
const totalNQueens = function (n) {
let res = 0;
const dfs = (n, row, col, pie, na) => {
if (row >= n) {
res++;
return;
}
// 将所有能放置 Q 的位置由 0 变成 1,以便进行后续的位遍历
// 也就是得到当前所有的空位
let bits = ~(col | pie | na) & ((1 << n) - 1);
while (bits) {
// 取最低位的1
let p = bits & -bits;
// 把P位置上放入皇后
bits = bits & (bits - 1);
// row + 1 搜索下一行可能的位置
// col | p 目前所有放置皇后的列
// (pie | p) << 1 和 (na | p) >> 1) 与已放置过皇后的位置 位于一条斜线上的位置
dfs(n, row + 1, col | p, (pie | p) << 1, (na | p) >> 1);
}
};
dfs(n, 0, 0, 0, 0);
return res;
};
class Solution {
public:
int totalNQueens(int n) {
vector<int> onePath;
int ret=0;
backTrac(n, onePath, 0, ret);
return ret;
}
void backTrac(int n, vector<int>& onePath, int row, int& ret)
{
if(onePath.size()==n)
{
ret++;
return;
}
for(int col =0; col <n; col++)
{
if(isValid(onePath, row, col))
{
onePath.push_back(col);
backTrac(n, onePath, row+1, ret);
onePath.pop_back();
}
}
}
bool isValid(vector<int>& onePath, int irow, int icol)
{
for(int i=0; i< onePath.size(); i++)
{
if(onePath[i] == icol)
return false;
if(onePath[i]+(irow-i) == icol)
return false;
if(onePath[i]-(irow-i) == icol)
return false;
}
return true;
}
};
答案虽然不长,但是。。 class Solution: def totalNQueens(self, n: int) -> int: def solve(row: int, columns: int, diagonals1: int, diagonals2: int) -> int: if row == n: return 1 else: count = 0 availablePositions = ((1 << n) - 1) & (~(columns | diagonals1 | diagonals2)) while availablePositions: position = availablePositions & (-availablePositions) availablePositions = availablePositions & (availablePositions - 1) count += solve(row + 1, columns | position, (diagonals1 | position) << 1, (diagonals2 | position) >> 1) return count
return solve(0, 0, 0, 0)
52. N 皇后 II
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/n-queens-ii/
前置知识
题目描述
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。
示例 1:
输入:n = 4 输出:2 解释:如上图所示,4 皇后问题存在两个不同的解法。 示例 2:
输入:n = 1 输出:1
提示:
1 <= n <= 9 皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。