51NOD n皇后问题2067

题目:

在 𝑛×𝑛 格( 𝑛≤8 )的国际象棋棋盘上摆放 𝑛 个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

输入:

一行一个正整数,表示 𝑛

输出数据:

输出一个正整数,表示 𝑛 皇后摆放的个数

样例:

输入:

8

输出:

92

解题思路:

首先我们看一个完全暴力的方法:从 8×8 的格子里选8个格子,放皇后,然后测试是否满足条件,若满足则结果加1,否则换8个格子继续试。很显然,64 选 8 的方案数为 4426165368,并不是个小数字。

稍加分析,我们可以得到另一个不那么暴力的方法:显然,每行每列最多只能有一位皇后,如果基于这个事实再进行暴力破解,那结果会好很多。安排皇后时,第一行有8种选法,一旦第一行选定,假设选为 (1,𝑖) ,那么第二行只能选 (2,𝑗) ,其中, 𝑗≠𝑖 ,所以有7种选法。以此类推,需要穷举的情况有 8!=40320 种,比十亿级别的小很多了。

之后我们尝试用回溯的方法来解决这个问题。

递归回溯本质上是一种枚举法。这种方法从棋盘的第一行开始尝试摆放第一个皇后,摆放成功后,递归一层,再遵循规则在棋盘第二行来摆放第二个皇后。如果当前位置无法摆放,则向右移动一格再次尝试,如果摆放成功,则继续递归一层,摆放第三个皇后......

如果某一层看遍了所有格子,都无法成功摆放,则回溯到上一个皇后,让上一个皇后右移一格,再进行递归。如果八个皇后都摆放完毕且符合规则,那么就得到了其中一种正确的解法。说起来有些抽象,我们来看一看递归回溯的详细过程。

第二层递归,尝试在第二行摆放第二个皇后(前两格被第一个皇后封锁,只能落在第三格)

第二层递归,尝试在第二行摆放第二个皇后(前两格被第一个皇后封锁,只能落在第三格)

第三层递归,尝试在第三行摆放第三个皇后(前四格被第一第二个皇后封锁,只能落在第五格)

第四层递归,尝试在第四行摆放第四个皇后(第一格被第二个皇后封锁,只能落在第二格)

第五层递归,尝试在第五行摆放第五个皇后(前三格被前面的皇后封锁,只能落在第四格)

由于所有格子都“绿了”,第六行已经没办法摆放皇后,于是进行回溯,重新摆放第五个皇后到第八格。

第六行仍然没有办法摆放皇后,第五行也已经尝试遍了,于是回溯到第四行,重新摆放第四个皇后到第七格。

继续摆放第五个皇后,以此类推......

以上就是 8​​ 皇后问题的经典解法。

我们来思考一下如何将这个过程转为 𝑐++ 程序。

首先我们采用一个二维数组来记录棋盘的情况,如果 𝑚𝑝[𝑖][𝑗]==1 则表示该位置有一个皇后,如果 𝑚𝑝[𝑖][𝑗]==0​ 则表示该位置是空的。

我们利用递归回溯来解决该问题。递归的过程是逐行试探,从第一行到第 𝑛 行。因此递归的深度是 𝑛。每段递归的过程(处理第 𝑖 行),试着向 𝑚𝑝[𝑖][1]−>𝑚𝑝[𝑖][𝑛]​ 放一个皇后。

由于是逐行枚举,因此不存在同一行放置多个皇后的情况。但每放置一个皇后,需要检查这一列上有没有放置其他皇后,2 个对角线也是如此。

可以使用 3 个循环来进行枚举。

代码:

#include <bits/stdc++.h>
using namespace std;
int n, ans = 0;
bool mp[15][15];
bool Check(int i, int j)
{
    for(int v = 1; v <= n; v++) {
        if(mp[v][j])
            return false; //检测之前放置的列
    }
    
    for(int v = 1; v < i && v < j; v++) {
        if(mp[i - v][j - v])
            return false; //检测左上到右下的斜线
    }
    
    for(int v = 1;v < i && j + v <= n; v++) {
        if(mp[i - v][j + v])
            return false;
    } 
    return true;
}
void Queen(int i)
{
    if (i == n + 1)
    {
        ans++;
        return;
    }
    for (int j = 1; j <= n; j++)
    {
        if (Check(i, j))
        {
            mp[i][j] = true;
            Queen(i + 1);
            mp[i][j] = false;
        }
    }
}
int main()
{
    cin >> n;
    Queen(1);
    cout << ans << endl;
    return 0;
}

已AC。

注:本文来自51NOD,为小罐头甜编辑。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值