leetcode --n皇后

今天刷leetcode时,碰到了n皇后问题,上网发现了一个非常给力的算法。

算法的核心:使用bit数组来代替以前由int或者bool数组来存储当前格子被占用或者说可用信息,从这


可以看出N个皇后对应需要N位表示。
巧妙之处在于:以前我们需要在一个N*N正方形的网格中挪动皇后来进行试探回溯,每走一步都要观察

和记录一个格子前后左右对角线上格子的信息;采用bit位进行信息存储的话,就可以只在一行格子也

就是(1行×N列)个格子中进行试探回溯即可,对角线上的限制被化归为列上的限制。
程序中主要需要下面三个bit数组,每位对应网格的一列,在C中就是取一个整形数的某部分连续位即可


row用来记录当前哪些列上的位置不可用,也就是哪些列被皇后占用,对应为1。
ld,rd同样也是记录当前哪些列位置不可用,但是不表示被皇后占用,而是表示会被已有皇后在对角线

上吃掉的位置。这三个位数组进行“或”操作后就是表示当前还有哪些位置可以放置新的皇后,对应0

的位置可放新的皇后。如下图所示的8皇后问题求解得第一步:
row:        [ ][ ][ ][ ][ ][ ][ ][*]
ld:         [ ][ ][ ][ ][ ][ ][*][ ]
rd:         [ ][ ][ ][ ][ ][ ][ ][ ]
------------------------------------
row|ld|rd:  [ ][ ][ ][ ][ ][ ][*][*]

所有下一个位置的试探过程都是通过位操作来实现的,这是借用了C语言的好处,详见代码注释。

我的leetcode代码:

class Solution {
public:
    int maxLimit;    //代表排序完毕的最大值,每位对应每列,如n=2,则 maxLimit = (1<<n)-1为3,即二进制11代表两列
    /*DFS求解,ans为最后vector结果,temp为每一组可行的解,row代表已占据的列情况,leftCross代表左对角线占用情况,rightCross代表右对角线占用情况  */
    void DFS(vector<vector<string>> &ans,vector<string> &temp,int row,int leftCross,int rightCross)
    {
        if(row==maxLimit)   //当每一列都有皇后即每一位都为1时,则将这组解存入结果vector
        {
           ans.push_back(temp);
           return ;
        }
        
        int cur,p,pos;
        string str;
        //获得可以将皇后放置的列,例如1110代表前三列可以放置,最后一列不可以放置
        cur = maxLimit&(~(row|leftCross|rightCross));
        while (cur) {
            //获得右数第一位不是0,即1的位置,此次在该位置放置一个皇后(-cur为负数,在内存里是以补码形式存储的,补码=原码(除符号位)取反+1)
2者相与刚好取得最后为1的bit,其他为0
            p = cur&(-cur);
            //将该位置置0,用于下一次迭代
            cur = cur - p;
            //保存str作为输出,例如p=0010,maxLimt=111,则pos=1010用于输出str
            pos=p+maxLimit+1;
            str = "";
            while(pos>1)
            {
                str += ((pos&1)?'Q':'.');
                pos>>=1;
            }
            //将str存入temp
            temp.push_back(str);
            //更改row,leftCross,rightCross信息,进行下次深度遍历迭代
            DFS(ans,temp,row+p,(leftCross+p)<<1,(rightCross+p)>>1);
            //弹出temp中的第一个str,使得下次循环更新新的str用于迭代
            temp.pop_back();
        }
    }


    vector<vector<string> > solveNQueens(int n)
    {
        
        vector<vector<string>> ans;
        vector<string> temp;
        maxLimit = (1<<n)-1;
        DFS(ans,temp,0,0,0);
        return ans;
    }
};

附上经典的解法用于对比:

#include <iostream>
using namespace std;
long sum=0;

bool Place(int k,int *p){//判断是否有冲突发生。k是当前摆放行
    for(int j=0;j<k;j++)//p[j]==p[k]说明第j行与第k行放在同一列
        if(abs(k-j)==abs(p[j]-p[k])||p[j]==p[k])

        //abs(k-j)==abs(p[j]-p[k])说明斜率为+-1,说明在同一列
            return false;
    return true;
}


void Backtrack(int t,int *p,int n){
    if(t>=n)//棋盘共有n行,用0~n-1表示,故如果t到达n,递归返回
        sum++;//增加一次成功次数
    else
        for(int i=0;i<n;i++){//对第t行进行摆放尝试,可能情况是0~n-1
            p[t]=i;
            if(Place(t,p))Backtrack(t+1, p,n);//如果无冲突,摆放t+1行
        }
}
int nQueen(int n){
    int *p=new int[n];//数组p[i]表示棋盘第i行皇后的位置
    for(int i=0; i<n;i++)
        p[i]=0;//将棋盘数组初始化为0;
    Backtrack(0, p, n);//回溯求解,从第0行开始摆放
    delete [] p;
    return sum;
}
int main(){
    cout<<nQueen(8);
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值