- N-Queens
The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.
Given an integer n, return all distinct solutions to the n-queens puzzle.
Each solution contains a distinct board configuration of the n-queens’ placement, where ‘Q’ and ‘.’ both indicate a queen and an empty space, respectively.
- col(n):用来记录第i列是否有棋子
- diagonal(2n):用来记录某条正对角线上是否有棋子。正对角线的棋子,i-j是一样的,为了避免出现负数,用n+i-j 来记录
- back_diagonal(2n):用来记录某条反对角线上是否有棋子。反对角线的棋子,i+j是一样的,范围是0~2(n-1)
最开始写的时候,多写了一个数据结构row(n),用来记录某一行是否有棋子,并且遍历时逐行判断,增加了开销。n=9时运行超时。正确的做法是,将棋子从上往下填,所以本次遍历要找的棋子在第m行,直接判断本行的所有棋子,减少开销。
vector<vector<string>> res;
vector<bool> col;
vector<bool> diagonal;
vector<bool>back_diagonal;
vector<vector<int>> cur;
void traceback(int n,int m){
if(n==m){
vector<string>temp;
for(int i=0;i<n;i++){
string s="";
for(int j=0;j<n;j++){
if(cur[i][j]==1){
s+='Q';
}else{
s+='.';
}
}
temp.push_back(s);
}
res.push_back(temp);
}
for(int i=0;i<n;i++){
if(col[i]||diagonal[n+(m-i)]||back_diagonal[i+m]){
continue;
}
col[i]=true;
diagonal[n+(m-i)]=true;
back_diagonal[i+m]=true;
cur[m][i]=1;
traceback(n,m+1);
cur[m][i]=0;
col[i]=false;
diagonal[n+(m-i)]=false;
back_diagonal[i+m]=false;
}
}
vector<vector<string>> solveNQueens(int n) {
col.resize(n);
diagonal.resize(2*n);
back_diagonal.resize(2*n);
cur.resize(n,vector<int>(n));
traceback(n,0);
return res;
}
2.位运算
(虽然自己写了一遍,但是应该很快就忘记)
先要知识:
- int __builtin_ctz (int x) 返回二进制表示的x后面的0的个数
- x & (−x) 可以获得 x的二进制表示中的最低位的 1的位置;
- x & (x-1)可以将 x的二进制表示中的最低位的 1置成 0。
col的二进制表示对应位上是否放了棋子(最低位表示第0列)
diagonal 从正对角线角度出发,表示某一列能否放棋子。
back_diagonal 从反对角线出发,表示某一列能否放棋子
vector<vector<string>> res;
vector<int> cur;
void traceback(int n,int row,int col,int diagonal,int back_diagonal){
//获取结果的优化
//cur[i]存放的数据是,第i行棋子的位置。先初始化某一行全都是. 再把对应位置修改为Q
if(row==n){
vector<string>temp;
for(int i=0;i<n;i++){
string s=string(n,'.');
s[cur[i]]='Q';
temp.push_back(s);
}
res.push_back(temp);
}
int availableposition=((1<<n)-1)&(~(col|diagonal|back_diagonal));
//(1<<n)-1 使低n位为1
//~(col|diagonal|back_diagonal) 若三个条件中,某个位置任意一个条件是1,最终结果反映为1,取反,用1表示能放棋子,0表示不能放棋子。
//和前面一个式子与,取低n位的结果(back_diagonal左移可能会超过n位)
while(availableposition!=0){
//值不为0,表示仍存在至少一位为1,可以放置棋子
int temp=availableposition&(-availableposition);
//结果的二进制形式,只有最低位1对应位置是1,其余位置是0
//与三个标识变量或,表示该位放置棋子(不直接在变量上修改,避免回溯时再次修改)
int pos=__builtin_ctz(temp);//求temp二进制末尾0的个数,即放置棋子的位置的十进制数
cur[row]=pos;
traceback(n,row+1,col|temp,(diagonal|temp)>>1,(back_diagonal|temp)<<1);
//对于下一行,主对角线右移,反对角线左移
cur[row]=-1;
availableposition=availableposition&(availableposition-1);//最低位1置0,表示最低位置已经搜索过了
}
}
vector<vector<string>> solveNQueens(int n) {
cur.resize(n);
traceback(n,0,0,0,0);
return res;
}