八皇后 (搜索-位运算)

题目描述

检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

这里写图片描述

上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 6

列号 2 4 6 1 3 5

这只是跳棋放置的一个解。请编一个程序找出所有跳棋放置的解。并把它们以上面的序列方法输出。解按字典顺序排列。请输出前3个解。最后一行是解的总个数。

输入格式
一个数字N (6 <= N <= 13) 表示棋盘是N x N大小的。

输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。


经典搜索问题,刚好开始学状压,借着机会练一练位运算。

我们枚举每一行,对于当前行:我们用二进制数 l 的第 i 位 表示第 i 列是否有棋子;dx 和 dy 的第 i 位分别表示该行第 i 个格子对应的左对角线和右对角线上是否有棋子。如:

n=6,
这里写图片描述(l=row,dx=ld,dy=rd)

应该可以理解。

显然,我们要得到的最终状态是 l=( 1 << n ) -1 = 111111 。

基本套路:

void dfs(int l,int dx,int dy)
{   
    if(l==standard_){//standard_=(1<<n)-1;
        ++cnt;//合法方案。
        return;
    }
    int pos=standard_&~(l|dx|dy);//~(l|dx|dy)操作得出的二进制数中,1表示这一位可以取。由于(l|dx|dy)前面有很多个0,并不都是可以取的,所以要&standard_以限定范围。
    for(;pos;pos-=pos&(-pos)) //lowbit运算,取出pos在二进制下最低位的1以及它后面的0构成的数。
    {
        int p=pos&(-pos);
        dfs(l+p,(dx+p)<<1,(dy+p)>>1);
        //+p操作即为在该格子中放上棋子。
        //(dx+p)<<1左移1位的原因是,枚举下一行时这一行第一格的左对角线将没有用处,而下一行最后一格的左对角线是新加入的,故要左移一位。至于最前面多出的0pos初值运算时会被处理掉。
        //右对角线同理。
    }       
}

ok。这题还要求我们输出字典序最小的前三个解。而我们在枚举每一行时是从右往左填的(因为lowbit取的是最右端的1),那么我们得到的反而是字典序最大。这个情况其实很好处理,只需要将棋盘右上角当作 [ 1,1 ]。

我们开一个 ans 数组,ans[ i ] 表示第 i 行 的棋子放在第几列。但是我们的 p 得到的是一个1后跟一串0,怎么知道这个 1 在第几列呢?可以抛弃 lowbit 运算一位位推,我是开了 map,map[ j ]表示 1 << j 共有几位(也就是从右往左数 1 在第几位),直接使用即可。

#include<bits/stdc++.h>
using namespace std;

int n;
int standard_;

int bit_;//第几行。
int ans[15];

int cnt;

map<int,int>mp;


inline void read(int &x)
{
    x=0;int f=1;char s=getchar();
    for(;s<'0'||s>'9';s=getchar()) if(s=='-') f=-1;
    for(;s>='0'&&s<='9';s=getchar()) x=(x<<3)+(x<<1)+s-48;
    x*=f;
}

void dfs(int l,int dx,int dy)
{   
    if(l==standard_){
        ++cnt;
        if(cnt<=3){
            for(int i=1;i<n;++i) printf("%d ",ans[i]);
            printf("%d\n",ans[n]);
        }
        return;
    }
    int pos=standard_&~(l|dx|dy);
    for(;pos;pos-=pos&(-pos))
    {
        int p=pos&(-pos);
        ans[++bit_]=mp[p];//原本应该是 n-mp[p]+1 ,但是我们把右上角当[1,1]之后,mp[p]表示的就是它在第几列了。
        dfs(l+p,(dx+p)<<1,(dy+p)>>1);
        ans[bit_--]=0;//回溯,注意--的位置啊,敲黑板。
    }       
}

int main()
{
    read(n);

    standard_=(1<<n)-1;
    for(int i=0;i<n;++i) mp[1<<i]=i+1;
    bit_=0;
    memset(ans,0,sizeof(ans));

    dfs(0,0,0);
    printf("%d",cnt);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/hfl030/article/details/80113842
今日推荐