继续思考“n-queens”问题
这次我们不是输出皇后的排列情况,而是输出n皇后问题一共有多少种解法
注:n 皇后问题是在 n*n 棋盘上放置 n 个皇后, 并且使它们互相不在对方的攻击范围之内的摆放方法;
注注:皇后的攻击范围是上、下、左、右、左上、左下、右下、右上共 8 个方向, 且不限距离。
public class Solution {
private int res;
private boolean[] col;
private boolean[] dia1;
private boolean[] dia2;
public int totalNQueens(int n) {
if (n == 0)
return res;
col = new boolean[n];
dia1 = new boolean[2 * n - 1];
dia2 = new boolean[2 * n - 1];
putQueen(n, 0);
return res;
}
private void putQueen(int n, int index) {
if (index == n) {
res++;
return;
}
for (int i = 0; i < n; i++) {
if (!col[i] && !dia1[index + i] && !dia2[index - i + n - 1]) {
col[i] = true;
dia1[index + i] = true;
dia2[index - i + n - 1] = true;
putQueen(n, index + 1);
col[i] = false;
dia1[index + i] = false;
dia2[index - i + n - 1] = false;
}
}
}
}
int vis[3][2 * 13], res;
void dfs(int cur, int n){
if(cur == n) {res++; return;}
for(int i = 0; i < n; i++){
if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]){
vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1;
dfs(cur+1, n);
vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0;
}
}
}
int totalNQueens(int n) {
memset(vis, 0, sizeof(vis));
res = 0;
search(0, n);
return res;
}
class Solution {
public:
int count = 0;
int x[9];
int totalNQueens(int n) {
backtrack(1,n);
return count;
}
void backtrack(int t,int n)
{
if(t>n)
count++;
else{
for(int i=1;i<=n;i++)
{
x[t] = i;
if(place(t))
backtrack(t+1,n);
}
}
}
bool place(int t)
{
for(int i=1;i<t;i++)
if(abs(x[t]-x[i]) == abs(t-i) || x[t] == x[i])
return false;
return true;
}
}; public class Solution {
int sum=0;
int[] x;
public int totalNQueens(int n) {
x = new int[n+1];
backtrack(1,n);//回溯算法开始,第一个皇后放下
return sum;
}
void backtrack(int t,int num){
if(t>num) //num为皇后的数目
sum++;//sum为所有的可行的解
else{
for(int i = 1;i<=num;i++){//每个皇后都遍历所有点,O(n2)
x[t] = i;//寻找这个皇后可以放置的所有点
if(place(t))
backtrack(t+1,num);//如果成立,进入下一级递归,放下一个皇后
} //如果不成立,放其他位置,若都无法放,跳回上级递归
}
}
boolean place(int k){
for(int j = 1;j<k;j++)
if(Math.abs(x[k] - x[j]) == Math.abs(k-j)||x[j] == x[k])
return false;
return true;
}
}
class Solution {
public:
int totalNQueens(int n) {
int res=0;
vector<string> cur(n, string(n, '.')); // 初始化棋盘,所有的位置都没有摆放皇后
dfs(cur, n, 0,res);
return res;
}
void dfs(vector<string> &cur, int &n, int row,int &res) {
if (row == n) { // 当当前行数超出了棋盘,则把这次搜索的结果放入res中。
res++;
return;
}
for (int j = 0; j < n; j++) {
if (isValid(cur, n, row, j)) { // 判断在(row, j)处是否可以放一个皇后
cur[row][j] = 'Q'; // 如果可以,则放一个皇后
dfs(cur, n, row + 1,res); // 继续在下一行找一个位置放皇后
cur[row][j] = '.'; // 因为需要找到所有可能的情况,所以必然需要对每一行进行回退。去判断这一行的下一列是否可以放皇后。
}
}
}
bool isValid(vector<string> &cur, int &n, int row, int col) {
//因为是一行放一个皇后,所以不用检查行
// 检查列
for (int i = 0; i < row; i++) {
if (cur[i][col] == 'Q') {
return false;
}
}
// 检查反斜线
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (cur[i][j] == 'Q') {
return false;
}
}
// 检查斜线
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (cur[i][j] == 'Q') {
return false;
}
}
return true;
}
}; class Solution {
public:
int totalNQueens(int n) {
ans_ = cols_ = diag1_ = diag2_ = 0;
DFS(0, n);
return ans_;
}
private:
int ans_, cols_, diag1_, diag2_;
bool available(int x, int y, int n) {
return !(cols_ & 1 << x ||
diag1_ & 1 << (x + y) ||
diag2_ & 1 << (n - 1 + x - y));
}
void DFS(int y, int n) {
if (y == n) {
++ans_;
return;
}
for (int x = 0; x < n; ++x) {
if (not available(x, y, n)) continue;
cols_ |= 1 << x;
diag1_ |= 1 << (x + y);
diag2_ |= 1 << (n - 1 + x - y);
DFS(y + 1, n);
cols_ -= 1 << x;
diag1_ -= 1 << (x + y);
diag2_ -= 1 << (n - 1 + (x - y));
}
}
}; int count;
public int totalNQueens(int n) {
count = 0;
boolean col[] = new boolean[n+1]; //列
boolean tip1[] = new boolean[n*2+1]; //右对角斜线 2~2n
boolean tip2[] = new boolean[n*2+1]; //左对角斜线 2~2n
nQueens(n, 1, col, tip1, tip2);
return count;
}
public void nQueens(int n, int cou, boolean col[], boolean tip1[], boolean tip2[]){
if(cou > n){
count++;
return;
}
for(int i=1; i<=n; i++){
if(!col[i] && !tip1[cou+i] && !tip2[n-cou+1+i]){
col[i] = true;
tip1[cou+i] = true;
tip2[n-cou+1+i] = true;
nQueens(n, cou+1, col, tip1, tip2);
col[i] = false;
tip1[cou+i] = false;
tip2[n-cou+1+i] = false;
}
}
}
class Solution {
public:
void f(int &cnt, int a[], int n, int level){
if(level==n){cnt++;return;}
for(int i=0;i<n;i++){
int j=0;
for(;j<level;j++){
if(a[j]==i || level-j==i-a[j] || j-level==i-a[j])break;
}
if(j==level){
a[level]=i;
f(cnt,a,n,level+1);
}
}
}
int totalNQueens(int n) {
int a[n];
int cnt=0;
f(cnt, a,n,0);
return cnt;
}
};
class Solution(object):
def solve(self, n, i, m, l0, l1, l2):
if i == n:
return [m[:]]
res = []
for j in range(n):
if j not in l0 and i + j not in l1 and j - i not in l2:
m.append(j)
l0[j] = 1
l1[i + j] = 1
l2[j - i] = 1
res.extend(self.solve(n, i + 1, m, l0, l1, l2))
m.pop()
l0.pop(j)
l1.pop(i + j)
l2.pop(j - i)
return res
def totalNQueens(self, n):
res = self.solve(n, 0, [], {}, {}, {})
return len(res)
/**
* don't need to actually place the queen,
* instead, for each row, try to place without violation on
* col/ diagonal1/ diagnol2.
* trick: to detect whether 2 positions sit on the same diagnol:
* if delta(col, row) equals, same diagnol1;
* if sum(col, row) equals, same diagnal2.
*/
private final Set<Integer> occupiedCols = new HashSet<Integer>();
private final Set<Integer> occupiedDiag1s = new HashSet<Integer>();
private final Set<Integer> occupiedDiag2s = new HashSet<Integer>();
public int totalNQueens(int n) {
return totalNQueensHelper(0, 0, n);
}
}
private int totalNQueensHelper(int row, int count, int n) {
for (int col = 0; col < n; col++) {
if (occupiedCols.contains(col))
continue;
int diag1 = row - col;
if (occupiedDiag1s.contains(diag1))
continue;
int diag2 = row + col;
if (occupiedDiag2s.contains(diag2))
continue;
// we can now place a queen here
if (row == n-1)
count++;
else {
occupiedCols.add(col);
occupiedDiag1s.add(diag1);
occupiedDiag2s.add(diag2);
occupiedCols.add(col);
occupiedDiag1s.add(diag1);
occupiedDiag2s.add(diag2);
count = totalNQueensHelper(row+1, count, n);
// recover
occupiedCols.remove(col);
occupiedDiag1s.remove(diag1);
occupiedDiag2s.remove(diag2);
}
}
return count;
}
}
回溯法,以行为基准进行回溯,如果当前行列摆放皇后与之前的冲突,则不继续回溯,否则,继续下一行的回溯。
代码如下:
//
// Created by jt on 2020/9/29.
//
#include <vector>
using namespace std;
class Solution {
public:
/**
*
* @param n int整型
* @return int整型
*/
int totalNQueens(int n) {
// write code here
int res = 0;
vector<int> vec;
backtrack(vec, res, n);
return res;
}
void backtrack(vector<int> &vec, int &res, int n) {
if (vec.size() == n) { ++res; return; }
for (int col = 0; col < n; ++col) {
vec.push_back(col);
if(!detectConflict(vec)) backtrack(vec, res, n);
vec.pop_back();
}
}
bool detectConflict(vector<int> &vec) {
int curRow = vec.size() - 1, curCol = vec[curRow];
for (int row = 0; row < curRow; ++row) {
if (curCol == vec[row]) return true;
if (curRow - row == abs(curCol - vec[row])) return true;
}
return false;
}
};
public int totalNQueens (int n) {
// write code here
int res = 0;
int[] col = new int[n]; //一行只有一个,只需要记录该列皇后位置
int[] leftLine = new int[2 * n - 1]; //从左往右方向的对角线,当同一个对角线和相同
int[] rightLine = new int[2 * n - 1]; //从右往左方向的对角线,同一个线r1-r2==l1-l2,或者保证索引为正数是(n-1)-(r1-l1)==(n-1)-(r2-l2)
res = queen(0, n, res, col, leftLine, rightLine);
return res;
}
public int queen(int restCol, int n, int res, int[] col, int[] leftLine, int[] rightLine) {
if (restCol == n) {
res++;
return res;
}
for (int restRaw = 0; restRaw < n; restRaw++) {
if (col[restRaw] == 0 && leftLine[restRaw + restCol] == 0 && rightLine[n - 1 - restRaw + restCol] == 0) {
col[restRaw] = 1;
leftLine[restRaw + restCol] = 1;
rightLine[n - 1 - restRaw + restCol] = 1;
res = queen(restCol + 1, n, res, col, leftLine, rightLine);
col[restRaw] = 0;
leftLine[restRaw + restCol] = 0;
rightLine[n - 1 - restRaw + restCol] = 0;
}
}
return res;
} class Solution: res = 0 def totalNQueens(self , n): self.row = [0]*n self.dig1 = [0]*(2*n-1) self.dig2 = [0]*(2*n-1) self.dfs(n,0) return self.res def dfs(self,n,step): if n == step: self.res += 1 return for i in range(n): if not self.row[i] and not self.dig1[step - i] and not self.dig2[i+step]: self.row[i],self.dig1[step - i],self.dig2[i+step] = 1,1,1 self.dfs(n,step+1) self.row[i],self.dig1[step - i],self.dig2[i+step] = 0,0,0
int[] xMark;
int result = 0;
public int totalNQueens (int n) {
// write code here
if(n == 0){
return 0;
}
//该数据用于记录每一列对应的皇后所在行
xMark = new int[n];
putQueen(0,n);
return result;
}
public void putQueen(int cur,int queenNums){
//遍历到尾
if(cur == queenNums){
result++;
}else{
//这里循环用于遍历cur列皇后放置在某一行的情况
for(int i = 0;i < queenNums;i++){
//将当前位置填入
xMark[cur] = i;
//如果该列能放置,即行/列/正反斜线都符合
if(place(cur)){
//放入成功,进入下一遍历
putQueen(cur+1,queenNums);
}
//回溯,回溯想法是将xMark[cur]赋值为下一行下标
}
}
}
public boolean place(int cur){
//这里只用到cur,因为后面的未放置,比较不了
for(int i = 0;i < cur;i++){
//第一个==,用于判断正反斜线是否有皇后,利用y2-y1/x2-x1 斜率等于正负1的思想
//第二个等号用于判断是否出现在同一行
//两个条件是或关系
if(Math.abs(xMark[cur] - xMark[i]) == Math.abs(cur - i)
|| xMark[cur] == xMark[i]){
return false;
}
}
return true;
} 思路:回溯法
因为每一行、每一列必定有一个Queen,对于第 i 行只需判断Queen可放在哪一列
需要判定该列、该正对角线、该负对角线是否被占用
col = new boolean[n];
dia1 = new boolean[2*n-1];
dia2 = new boolean[2*n-1];
对于同一条负对角线的规律:i + j = 固定的数 [0, 2*n-1]
对于同一条正对角线的规律:j - i + n - 1 = 固定的数 [0, 2*n-1]
import java.util.*;
public class Solution {
/**
*
* @param n int整型
* @return int整型
*/
private boolean[] col,dia1,dia2;
private int res;
public int totalNQueens (int n) {
// write code here
col = new boolean[n];
dia1 = new boolean[2*n-1];
dia2 = new boolean[2*n-1];
Queen(0,n);
return res;
}
void Queen(int k,int n)
{
//k表示候选的列数
if(k==n)
{
res++;
}
else
{
for(int i=0;i<n;i++) //第i行
{
if(!col[i] && !dia1[i+k] && !dia2[k-i+n-1])
{
col[i]=true;
dia1[i+k]=true;
dia2[k-i+n-1]=true;
Queen(k+1,n);
col[i]=false;
dia1[i+k]=false;
dia2[k-i+n-1]=false;
}
}
}
}
} int ans;
vector<bool>f1, f2, f3;
void dfs(int n, int I) {
if (I == n) {
ans++;
return;
}
for (int i = 0; i < n; i++) {
if (!f1[i] && !f2[I + i] && !f3[i + n - I - 1]) {
f1[i] = 1, f2[I + i] = 1, f3[i + n - I - 1] = 1;
dfs(n, I + 1);
f1[i] = 0, f2[I + i] = 0, f3[i + n - I - 1] = 0;
}
}
}
int totalNQueens(int n) {
if (n <= 1)return n;
for (int i = 0; i < n; i++)f1.push_back(0);
for (int i = 0; i < n + n; i++)f2.push_back(0), f3.push_back(0);
ans = 0;
dfs(n, 0);
return ans;
} bool isValid(vector<string> &temp, int row, int col, int &n) {
for (int i = row - 1; i >= 0; --i) {
if (temp[i][col] == 'Q') return false;
}
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; --i, --j) {
if (temp[i][j] == 'Q') return false;
}
for (int i = row - 1, j = col + 1; i >= 0 && j<n; --i, ++j) {
if (temp[i][j] == 'Q') return false;
}
return true;
}
void dfs(int &cnt, vector<string> &temp, int row, int &n) {
if (row == n) {
cnt++;
return;
}
for (int j = 0; j<n; ++j) {
if (isValid(temp, row, j, n)) {
temp[row][j] = 'Q';
dfs(cnt, temp, row + 1, n);
temp[row][j] = '.';
}
}
}
int totalNQueens(int n) {
int cnt=0;
vector<string> temp(n, string(n, '.'));
dfs(cnt, temp, 0, n);
return cnt;
} class Solution {
public:
int arr[100];
int ans;
Solution():ans(0){}
int totalNQueens(int n) {
for(int i=0;i<n;i++)
getans(n,0,i);
return ans;
}
void getans(int n,int nowrow,int nowcol){
if(nowrow==n-1&&check(arr,nowrow,nowcol)){
ans++;
}
else{
if(check(arr,nowrow,nowcol)){
arr[nowrow]=nowcol;
for(int i=0;i<n;i++)
getans(n,nowrow+1,i);
}
}
}
bool check(int arr[],int nowrow,int nowcol){
for(int i=0;i<nowrow;i++){
if(nowcol==arr[i]||abs(nowrow-i)==abs(nowcol-arr[i]))
return false;
}
return true;
}
}; class Solution {
public:
int totalNQueens(int n) {
int res = 0;
vector<int> temp(n);
solveNQueens(res, temp, 0, n);
return res;
}
void solveNQueens(int &res, vector<int> &temp, int row, int n) {
if (row == n) {
res++;
return;
}
for (int i = 0;i < n;i++) {
temp[row] = i;
if (canPlaceQueen(temp, row)) {
solveNQueens(res, temp, row + 1, n);
}
}
}
bool canPlaceQueen(vector<int> &temp, int row) {
for (int i = 0;i < row;i++) {
if (temp[i] == temp[row] || abs(temp[i] - temp[row]) == abs(row - i)) {
return false;
}
}
return true;
}
};