b_lc_N皇后(一列一列地放,放完就检查)
构造n皇后
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
输入:4
输出:[
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。
思路
回溯放置皇后 'Q' 和棋子 '.',放完整个棋盘后就对于每一种放置方案都检查是否合法,这是最朴素的做法;
剪枝:每放完一列就去检查当前方法是否合法(这里为了方便选择每次放完一列)
const int N=100;
class Solution {
public:
int n; char g[N][N];
vector<vector<string>> ans;
bool chk(int x, int y) {
for (int i=x-1; i>=0; i--) {
int d=x-i;
if (g[i][y]=='Q' || (y-d>=0 && g[i][y-d]=='Q') || (y+d<n && g[i][y+d]=='Q'))
return false;
}
return true;
}
void dfs(int i) {
if (i==n) {
vector<string> now;
for (int i=0; i<n; i++) {
string s;
for (int j=0; j<n; j++)
s+=g[i][j];
now.push_back(s);
}
ans.push_back(now);
return;
}
for (int j=0; j<n; j++) if (chk(i, j)) {
g[i][j]='Q';
dfs(i+1);
g[i][j]='.';
}
}
vector<vector<string>> solveNQueens(int n) {
this->n=n;
memset(g,'.',sizeof g);
dfs(0);
return ans;
}
};
复杂度分析
- Time:\(O(2^n)\),
- Space:\(O(...)\),
题外话:
- 主对角线特点:所有格子格子的行下标与列下标之差相同
- 辅对角线特点:所有格子格子的行下标与列下标之和相等
合法的序列
Q1,Q2,…,QN 来表示一种棋盘摆放,其中 Qi 表示第 i 列的皇后所在的行号。
疑惑
不知道为什么用一个map来记录两条对角线为什么不行(加了10w的偏置值也不行)
#include<bits/stdc++.h>
using namespace std;
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int q; cin>>q;
for (int i=0; i<q; i++) {
int m,valid=1;
cin>>m;
unordered_map<int, bool> mx, my, md1, md2;
for (int y=1; y<=m; y++) {
int x; cin>>x;
if (mx[x] || my[y] || md1[x+y] || md2[x-y]) {
valid=0;
}
mx[x]=my[y]=md1[x+y]=md2[x-y]=1;
}
cout<<(valid ? "YES" : "NO")<<'\n';
}
return 0;
}
N皇后II
给定一个棋盘大小n,返回n皇后不同的解决方案的数量。
思路:记录每一行放下的皇后的位置
class Solution:
ans=0
def totalNQueens(self, n: int) -> int:
def valid(x,row):
for c in range(x):
if row[c]==row[x] or abs(x-c)==abs(row[x]-row[c]):
return False
return True
def dfs(x,row):
if x==n:
self.ans+=1
return
for y in range(n):
row[x]=y
if valid(x,row):
dfs(x+1,row)
row=[0]*n
dfs(0,row)
return self.ans