N皇后问题 (史上最快解法 位运算)

欢迎访问https://blog.csdn.net/lxt_Lucia~~

宇宙第一小仙女\(^o^)/~萌量爆表求带飞=≡Σ((( つ^o^)つ~ dalao们点个关注呗~

 

--------------------------------我只是一条可爱哒分界线-------------------------------

 

今天在论坛上有幸看到了一个难得一见的算法,运用了位运算,应该是目前为止N皇后问题的最快解法了。

 

一、问题:

Description

在 n×n 格的棋盘上放置彼此不受攻击的n 个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在n×n格的棋盘上放置n个皇后,任何 2 个皇后不放在同一行或同一列或同一斜线上。 设计一个解n 后问题的队列式分支限界法,计算在n× n个方格上放置彼此不受攻击的n个皇后的一个放置方案。

 

Input

共有若干行,每行一个正整数 N≤20,表示棋盘和皇后的数量;如果N=0,表示结束。

 

Output

共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。

 

Sample Input

1

8

5

0

 

Sample Output

1

92

10

 

二、题意:

中文题,不解释。注意斜着也不可以在同一直线上,包括 左斜 (斜率为1) 和 右斜 (斜率为-1)。

 

三、思路:

核心的是在针对试探-回溯算法所用的数据结构的设计上。
程序采用了递归,也就是借用了编译系统提供的自动回溯功能。

算法的核心:使用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语言的好处,详见代码注释。

 

四、代码:

#include 
#include 
 
using namespace std;
typedef long long ll;
 
ll a[11], sum, mark;
 
void test(ll row, ll ld, ll rd)
//row表示已经有皇后的行
//ld、rd分别表示已经有皇后的左右斜列,每一位表示该列中可以摆放皇后的位置,1表示可以摆放,0表示不可以摆放。row在每查找下一列时不用改动,ld、rd需要分别左移和右移一位。
{
    if(row != mark) //当所有行都有皇后时说明摆放完毕
    {
        ll pos = mark & ~(row | ld | rd); //pos的每一位表示该列中可以摆放皇后的位置,1表示可以摆放,0表示不可以摆放
        while(pos) //如果pos中还有可以摆放的位置
        {
            ll p = pos & -pos; //p为pos中最末一个可以摆放的位置
            pos -= p; //将p从可摆放位置去掉
            test(row + p, (ld + p) << 1, (rd + p) >> 1); //对下一列进行判断
        }
    }
    else
        sum++;
}
 
int main()
{
    int n;
    memset(a, 0, sizeof(a));
    while(~scanf("%d", &n) && n)
    {
        sum = 0, mark = 1;
        if(a[n] == 0)
        {
            mark = (mark << n) - 1; //有多少个皇后,就有多少bit被置1。(从低位到高位)
            test(0, 0, 0);
            a[n] = sum;
        }
        printf("%lld\n", a[n]);
    }
}

 

参考数据:

n   a [n]
1 1
2 0
3 0
4 2
5 10
6 4
7 40
8 92
9 352
10 724
11 2680
12 14200
13 73712
14 365596
15 2279184
16 14772512
17 95815104
18 666090624
19 4968057848
20 39029188884
21 314666222712
22 2691008701644
23 24233937684440
24 227514171973736
25 2207893435808352

 

参考论坛:https://bbs.csdn.net/topics/80489768

--------------------------------我也是有底线的---------------------------------

宇宙第一小仙女\(^o^)/~萌量爆表求带飞=≡Σ((( つ^o^)つ~ dalao们点个关注呗~

你可能感兴趣的:(spiny,algorithm)