位运算实现N皇后问题

一、题意

要在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;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值