一、题意
要在n*n的格子上放下n个皇后,要求皇后的同一行,同一列,以及对角线不能有皇后。
解决n皇后问题的时间复杂度是O(n!),好的方法可以大量剪枝,大量优化常数时间
二、方法
第一种方法:用数组表示路径的方法(经典,常数时间慢,不推荐)
(1)记录之前每行的皇后放在了什么列;
(2)来到第i行的时候,可以根据0-i-1行皇后的位置,判断能放在什么列;
(3)把能放的列都尝试一遍,每次尝试修改路径数组表示当前的决策,后续返回的答案都累加返回
具体代码:
class Solution {
public:
int totalNQueens(int n) {
int a[n+1];
if(n<1){
return 0;
}
else{
return f(0,a,n);
}
}
int f(int i,int path[],int n){
if(i==n){
return 1;
}
int res=0;
for(int j=0;j<n;j++){
if(check(path,i,j)) { //看把皇后放在(i,j)位置是否冲突
path[i]=j;
res+=f(i+1,path,n);
}
}
return res;
}
bool check(int path[],int i,int j){
for(int k=0;k<i;k++){ //枚举之前皇后的编号
if(j==path[k]||abs(i-k)==abs(j-path[k])){
//如果之前的皇后的列和现在重合了,说明不能将它作为当前皇后的列
//怎么判断是否在对角线上,如果|当前行-之前行|==|当前列-之前列|,则说明两皇后在同一对角线上面
return false;
}
}
return true;
}
};
第二种方法:位运算
(1)int col: 0~i-1行皇后放置的位置因为正下方延伸的原因,哪些列不能再放皇后(1表示不能放,0表示可以放)
(2)int left: 0~i-1行皇后放置的位置因为左下方延伸的原因,哪些不能再放皇后(1表示不能再放,0表示可以再放)
(3)int right: 0~i-1行皇后放置的位置因为右下方延伸的原因,哪些位置不能再放皇后
(4)根据col,left,right,用位运算快速判断能放哪些列
(5)把能放的列都尝试一遍,每次尝试修改3个数字表示当前的决策,后续返回答案都累加返回
具体代码:
class Solution {
public:
int totalNQueens(int n) {
if(n<1){
return 0;
}
int limit=(1<<n)-1; //表示当前放皇后的规模
return f(limit,0,0,0);
}
//col:之前皇后的列影响
//left:之前皇后右上对角线到左下对角线影响
//right:之前皇后右对角线影响
int f(int limit,int col,int left,int right){
if(col==limit){
return 1;
}
int ban=col|left|right; //综合的限制
int candidate=limit&(~ban); //当前能放的位置
int place=0;
int ans=0;
while(candidate!=0){
place=candidate&(-candidate); //取出最右侧的1
candidate^=place; //消除最右侧的1
ans+=f(limit,col|place,(left|place)>>1,(right|place)<<1);
}
return ans;
}
};