N皇后问题的求解 — 回溯法C++实现

读完本文这个小故事,相信你会完全弄懂N皇后问题。

故事还得从中国象棋说起…

中国象棋 — 国王背后的女人

中国象棋中特别重要,一旦将被吃,则满盘皆输。这里我们暂且把将称为国王吧,国王虽然如此重要,但只能在田字格里移动,而且每次只能移动一格,尽管可以往横线和斜线的方向移动。

和他背后的女人 — 皇后相比,国王简直弱到不行:

在国际象棋中,皇后是最强大的一枚棋子,可以吃掉与其在同一行、列和斜线的敌方棋子。这简直比她没用的老公国王厉害不知多少倍。

国王的烦恼 — 安排皇后

然鹅现实就是这么令人啼笑皆非,一个国王可以有多个(N)皇后,但一个皇后只能有一个国王

国王的烦恼来了,如何将这N个夫人安置在后宫里,且让她们每个人都能和睦相处(不会有吃掉的情况发生)。

于是乎,国王开始了他的思考过程:

安排在一间屋子肯定是不行了,他们在战场上都可以吃掉与她们处在同一行,同一列和同一斜线的敌人,这样的话给她们住一排屋看样子也不行了。

算了,让小兵们给N个夫人们建造一个N*N个屋子吧,就排成正方形,让她们每个人住一排吧,以后我就只能在这一个个田字组成的后宫里走来走去看望夫人们了。(国王果然还是喜欢走田字)

过了不久的时间,手下告诉国王后宫建好了。于是,国王开始了安置夫人们的旅程。

国王的后宫 — N皇后

这里我们以4皇后的问题来举例说明

  • 一开始后宫是空的,对于第一个皇后,随便挑:
    在这里插入图片描述

  • 但是国王也讲究先来后到,于是国王把第1个皇后安排在了坐标为(1,1)的屋子里,这样一样,第1行和第1列里都不能住别的皇后了。对于第2行,第3列和第4列均可以放皇后:
    在这里插入图片描述

  • 于是国王叫来了第2位皇后,把她放在了第2行第3列,但是国王发现这样一来,第3行就不能放皇后了,于是国王立马让第2位皇后住在了第2行第4个屋:
    在这里插入图片描述

  • 聪明的你立马看出来了,第4行已经不能住任何皇后了,因为第3个皇后和第4个皇后的屋子处在一个斜线上。当然,国王也不傻,于是他赶紧让第2个皇后暂时先搬出来,并告诉皇后这个屋子风水不好,她不能住。皇后虽然很厉害,但也很听国王的话,于是第2个皇后立马搬了出来。后来,国王让第1个皇后住在了第1行第2列的屋子,然后国王也在考虑第2为皇后住哪的问题(第2行第4列绿色):
    在这里插入图片描述

  • 然后国王让第2位皇后住进了第2行第4列的屋子,同时国王已经在考虑第3位皇后的住宿问题了(第3行第1列绿色):
    在这里插入图片描述

  • 国王让第3位皇后,住在了第3行第1列的屋子,这时国王开始高兴了起来,因为国王发现第4位皇后的屋子也有了着落(第4行第3列绿色)
    在这里插入图片描述

  • 于是国王在将第3位皇后安排好后立马让第4为皇后住在了第4行第3列的屋子。国王的烦恼终于解决了,最终这4位皇后的屋子位置情况如下:
    在这里插入图片描述

国王的心事终于得到解决,于是便和皇后们没羞没臊了起来,此处略去不知多少字…

试问谁又不喜欢尝试新鲜呢?不知过了多少个日子,国王突然想给皇后们换个屋子,于是他又尝试了起来(这里不再赘述细节)

最终皇后们的屋子位置情况如下:
在这里插入图片描述

一句话概括N皇后问题

事实上,以上问题就是N皇后问题,在N*N的格子里放下N个皇后,并求解有多少中解法

4皇后问题只有以上2中解法

回溯法

国王在安排皇后们时不断尝试,如果在这一步发现不能解决问题,就回过头修改上一步的策略的问题其实就是回溯法

因此,回溯法徒有其名,剥去其华丽的外衣,它的本质是一种试探的方法:

对于某个问题的求解,先将当前的选择作为解的一部分,基于当前的解法继续求解,当试探到某一步时,发现原先的选择并不能达到目标,则退回去修改原来的选择。

这种走不通就退回再走的方法称为回溯法。

N皇后问题的回溯法C++求解

代码如下:

#include <iostream>
#include <cmath>
using namespace std;
int N,res=0;
int queen(int *arr,int r){
    if(r==N)
        res++;
    else{
        for(int c=0;c<N;c++){
            arr[r]=c;
            bool can=true;//用于标记是否可以放皇后
            for(int c1=0;c1<r;c1++){                //不在同一行
                if(arr[r]==arr[c1]||                //同一列的情况
                   abs(r-c1)==abs(arr[r]-arr[c1])){ //同一对角线的情况
                    can=false;
                    break;
                }
            }
            if(can)
                queen(arr,r+1);
        }
    }
    return res;
}
int main(){
    N=4;
    int arr[N];
    cout<<queen(arr,0);
    return 0;
}

以上代码运行结果为:

2

才疏学浅,难免有错误和不当之处,欢迎交流批评指正!
同时有问题的话欢迎留言或邮箱联系(ljt_IT@163.com)。

创作不易,觉得写得不错就微信扫码奖励一下吧!

Reward

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值