N皇后问题 求解方案数

这是个很经典的练习回溯法的问题。目前还没有已知数学公式可以直接计算,所以只能交给CPU暴力搜索了。

方案一:
直接搜索、回溯。对于每一行枚举皇后的位置,再开一个bool型数组记录哪一列、哪一条主对角线、哪一条副对角线已经放过皇后。速度上N <= 13还是能在1s之内跑出来的。

// 代码中id(x, y, 1)返回的是(x,y)所在行已经放过皇后的标号,实际没有任何卵用~.~

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

int n, ans;
bool vis[105];

int Dis(int x, int y, int a, int b)
{
    return abs(x-a)+abs(y-b);
}

int id(int x, int y, int k)
{
    if(k == 1) return x;
    if(k == 2) return n+y;
    if(k == 3) return n*2+Dis(x, y, 1, n)+1;
    if(k == 4) return n*4-1+Dis(x, y, 1, 1)+1;
}

bool visited(int x, int y)
{
    return vis[id(x, y, 2)] || vis[id(x, y, 3)] || vis[id(x, y, 4)];
}

void setting(int x, int y, bool c)
{
    vis[id(x, y, 2)] = vis[id(x, y, 3)] = vis[id(x, y, 4)] = c;
}

void dfs(int now)
{
    if(now > n) {ans++; return ;}
    for(int i = 1; i <= n; i++) if(!visited(now, i))
    {
        setting(now, i, 1); dfs(now+1); setting(now, i, 0);
    } 
}

int main()
{
    scanf("%d", &n);
    dfs(1);
    printf("%d", ans);
    return 0;
}

方案二:
这个方法是新学到的,状压位运算搜索。dfs(row, ld, rd),row表示的是列中已经放过皇后的位置,ld表示的是副对角线(撇),rd表示的是主对角线(捺)中已经放过皇后而导致在当前行的不能放置皇后的位置。当row == (1<< n)-1即所有列都有皇后时,就搜索到了一个解。tot&(~(row|ld|rd)) 是取出所有还能放置皇后的位置。pos&-pos是取出第一个1(相当于树状数组的lowbit函数)。这么做是快了很多,N=14时可以在1s内跑出来。虽然只比方案一N多加了1,但是对于N皇后这种阶乘级复杂度的问题来说,当N变大时,就快很多很多了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n, tot, ans;

void dfs(int row, int ld, int rd)
{
    if(row == tot) {ans++; return ;}
    int pos = tot&(~(row|ld|rd));
    while(pos)
    {
        int p = pos&-pos;
        pos -= p;
        dfs(row|p, (ld|p)<<1, (rd|p)>>1);
    }
}

int main()
{
    scanf("%d", &n);
    tot = (1<<n)-1;
    dfs(0, 0, 0);
    printf("%d", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值