您的位置:首页 > 其它

数学游戏(mathgame)解题方法 - N皇后位运算解法,14皇后亦在1秒中

2016-02-15 19:34 423 查看
桐桐的数学游戏(mathgame)

【问题描述】

相信大家都听过经典的“八皇后”问题吧?这个游戏要求在一个8×8的棋盘上放置8个皇后,使8个皇后互相不攻击(攻击的含义是有两个皇后在同一行或同一列或同一对角线上)。

桐桐对这个游戏很感兴趣,也很快解决了这个问题。可是,他想为自己增加一点难度,于是他想求出n皇后的解的情况。你能帮助他吗?

【输入格式】

输入仅有一个数n(1≤n≤13),表示为n皇后问题。

【输出格式】

输出仅有一个数,表示n皇后时问题的解法总数。

【输入样例】

8

【输出样例】

92

这道问题想必也是非常经典的回朔问题了,但是到现在仍有不少人研究它。我在网上顺路寻找位运算的时候,找到了小时后大概五年级左右非常喜欢的一本书的作者Matrix67的一篇文章,现贴出链接如下:

位运算简介及实用技巧(一):基础篇: http://www.matrix67.com/blog/archives/263

位运算简介及实用技巧(二):进阶篇(1): http://www.matrix67.com/blog/archives/264

位运算简介及实用技巧(三):进阶篇(2): http://www.matrix67.com/blog/archives/266

位运算简介及实用技巧(四):实战篇: http://www.matrix67.com/blog/archives/268

其中,关于本题解法的程序在(三)里面,试了一下,真的很厉害!连例程都不能比它好,而打表有时也比这还慢一点。亲自测试,发现13只用0.08秒左右,而14只用0.6秒,15只用2.8秒,是我见过的里面最快的。下面给出这位大神的程序(注:从Pascal翻译为C++,使用了GNU风格的缩进,请不要在意)[code]/* 本程序翻译自Matrix67的博客上的Pascal程序,地址为:http://www.matrix67.com/blog/archives/266 感谢Matrix67大神!这一程序异常高效 本程序已在GCC 4.9.2 下编译通过,并已经AC本道例题 */ #define _DUBUG #include <cstdio> using namespace std; int upperlim; unsigned long long int sum; void NQueen(int row,int ld,int rd) { int pos,p; if (row!=upperlim) { pos=upperlim & ~ (row | ld | rd); //找出不被占用的位,并设置为1,反之为0 while(pos!=0) //这个循环的循环次数应该是pos中1的个数 { p=pos & -pos; //相当于取pos二进制位最后一个1及其后面的0 pos=pos-p; //去掉那一部分 NQueen(row+p,(ld+p)<<1,(rd+p)>>1); //对放下一个皇后做下准备:将当前放棋子位置进一步占用的位置更新 } } else { ++sum; } } int main() { #ifndef _DEBUG freopen("mathgame.in","r",stdin); freopen("mathgame.out","w",stdout); #endif int n; scanf("%d",&n); upperlim=(1<<n)-1; //upperlim=(2^n)-1 sum=0; NQueen(0,0,0); printf("%d\n",sum); }



像这样的样例程序,进一步的展示出了位运算的高效。当然,这个程序实际上思路也不难。 首先,row表示列被占用的情况,ld和rd表示两类对角线,下面的row | ld | rd表示全部被占用的情况,占用的为1,取反是为后面去找1寻找未被占用的做准备,最后pos相当于是未被占用的一个二进制串。接下来这个循环里利用了位运算的基础知识,直接取其中的1,不用像平常那样枚举每一个位置再判断了。最后那个递归很难理解,但实际上第一个row+p指继续占用第p位,(ld+p)<<1,其中ld+p指当前的占用加上当前的位置,左移一位指偏移,这一颗棋子对下一行棋子的影响要平移,(rd+p)>>1以此类推。最后,当row=upperlim,即所有位置上的皇后都已放完时,就累计答案并结束递归。注意,在这里无须回朔,因为pos和p是局部变量。而这,就构成了这一个奇妙的位运算程序! 另外还有一种有限制的N皇后问题,程序如下(同时也是本次模拟题的第二题,不难,只改了几个地方):
#define _DUBUG
#include <cstdio>
using namespace std;

int a[20];
int upperlim;
unsigned long long int sum;
void NQueen(int row,int ld,int rd,int k)
{
int pos,tpos,p,tp;
if (row!=upperlim)
{
pos=upperlim & ~ (row | ld | rd | a[k]);
while(pos!=0)
{
p=pos & -pos;
pos=pos-p;
NQueen(row+p,(ld+p)<<1,(rd+p)>>1,k+1);
}
}
else
{
++sum;
}
}

int main()
{
freopen("queens.in","r",stdin);
freopen("queens.out","w",stdout);
int n;
char c;
scanf("%d",&n);
for (int i=0;i<n;++i){
a[i]=0;
for (int j=0;j<n;++j){
scanf("%c",&c);
if (c=='\n') scanf("%c",&c);
if (c=='.') a[i]+=1<<j;
}
}
upperlim=(1<<n)-1;
sum=0;
NQueen(0,0,0,0);
printf("%d\n",sum);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: