51. N-Queens(hard)

  1. 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;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值