位运算的魅力---N皇后问题

题目描述

有一个N*N的棋盘,有些格子可以放置皇后,一个皇后可以对它这一行的位置,这一列的位置,它所在的左对角线和右对角线攻击,这些位置不能放置皇后,一共放置N个皇后,问有多少种放置的方式。

输入输出格式

输入格式:

 

第一行有一个N。接下来有N行N列描述一个棋盘,“*”表示可放“.”表示不可放。

 

输出格式:

 

输出方案总数

solution

 这道题主要考察位运算,例如101001表示这一排第2、4、5个位置可以放置皇后。

我们用k进制(这里是二进制)来存储状态,这也是状态压缩的基本思想。

显然每行只能放置一个皇后,我们逐行枚举

 那么我们需要记录四个状态,一是这一行题目给定的状态,二是这一行中有哪些列会被之前摆放的皇后攻击,那么无法再摆放,三是左对角线的影响,四是右对角线的影响,这些状态只用每行一个数字的二进制来存储就行了。

判断时将这四种情况取并集,再取反,1的位置就成了可以放置的位置,利用lowbit进行DFS即可,具体的实现细节还要看代码

code

#include<cstdio>
#include<iostream>
#include<string>
#define re register
using namespace std;
int n,all,sta[50],ans;
string s;
int lowbit(int x)//找到最右边的1位 
{
    return x&-x;
} 
//取反是补码,与反码不同 ~(3)=-4;
void dfs(int now,int L,int R,int d)
{
    if(now==all){ans++;return;} 
    //当now==all的时候,说明全部摆放完毕,ans++ 
    //now表示到这一行的摆放情况,L表示左对角线的情况
    //R表示右对角线的情况,d记录现在在哪一行,
    //sta[d]表示第d行题目的限制条件 
    int pos=all&(~(now|L|R|sta[d])),p; //all是用来限制,把右对角线左移移出去的除掉 
    while(pos)//枚举每个可以放的位置 
    {
        p=lowbit(pos);
        pos-=p;//这一个位置即将枚举,删掉 
        dfs(now+p,(L+p)>>1,(R+p)<<1,d+1);//左对角线对下一行的影响是它的左下角,因此二进制右移
        //右对角线相反,别忘了加上p处放置的棋子的影响 
    }
}
int main()
{
    scanf("%d",&n);
    all=(1<<n)-1;//表示最终状态,比如 n=4 01111 
    for(re int i=1;i<=n;++i)
    {
        cin>>s;
        for(int j=1;j<=n;++j)
         if(s[j-1]=='.')sta[i]|=(1<<(n-j));//对这个数二进制每个位置进行操作 
    }
    dfs(0,0,0,1);//一开始没放,没有任何摆放过皇后限制 
    printf("%d",ans);
    return 0;
}

 

posted @ 2019-04-30 19:41  __Liuz  阅读(213)  评论(0编辑  收藏  举报