八皇后问题,秒懂递归回溯(有图详解|c语言)

article/2025/9/10 20:03:23

目录

👸🏻前言

👸🏻题目介绍

👸🏻引入:

👸🏻解决思路:

👸🏻理论存在,实践开始!

👸🏻难点1:如何表示对角线被占领?

👸🏻难点2:如何用递归的方法来放皇后?

👸🏻难点3:如何实现回溯?

👸🏻难点4:如何实现皇后位置的输出?

👸🏻全部代码如下:

👸🏻总结: 

Love is worth years.❤热爱可抵岁月漫长。


前言

各位和我一样的刚学完递归的小白们,是不是突然遇见了一个大BOSS,八皇后👸🏻问题!!把自信的说着“老子递归学好了!”的你一棒子打回了出生点,就像你刚玩只狼遇到的那个大胖子,刚玩原神遇到的雪山。今天,我就和大家一起学习一下这个著名的八皇后👸🏻问题。

29035e22acd844fe8ab6217fe6aaa173.gif


题目介绍

八皇后问题,是一个古老而著名的问题,是回溯算法的典型例题。该问题是十九世纪著名的数学家高斯1850年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法并输出每一种摆法。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYzByZQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 升华:我在做这个东西的时候本来想用人脑将他们摆进去,发现太!麻!烦!可能这就是计算机存在的意义吧!


引入:

不知道大家有没有在前面学习递推的时候学过马拦过河卒的问题没看过的可以点这,这个问题和那个问题都是一个棋类的问题,他们也有相同之处,都是需要用一个数组来表示这个地方有没有被占领,象棋的那个题是马占领的地方需要标记,而这个题是皇后占领的每一行每一列每一个对角线都需要标记。所以我们可以采用一些个数组来表示这个地方被皇后占领了当然这个题还用到了一个重要的算法就是递归算法和回溯的思想


解决思路:

1.首先定义三个数组分别表示列被占领,左对角线被占领,和右对角线被占领。因为我们一行一行的放进去所以不用考虑行的问题。👸🏻

2.利用递归算法在没有被占领的地方一行一行的放入我们的皇后。👸🏻

3.利用递归和回溯算法一行一行的放入皇后。👸🏻


理论存在,实践开始!

难点1:如何表示对角线被占领?

用数组来表示列被占领很简单,只要让皇后所在的那一列的数组的值都等于0,就可以解决这个问题,但如何用数组来表达对角线呢??你知道吧!你数一数,这一共有多少个对角线?一共有15条对角线,所以我们可以定义两个数组d1和d2来用来表示两个方向的对角线。如果被占领就是0没有被占领就是1

定义代码如下:

int d1[15]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};//定义上对角线
int d2[15]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} ;//定义下对角线

如何在摆放的时候来确定哪一行哪一列呢?这样定义十五行太好定义了但是想要表示就难了,我们来研究一下这个东西。看下图,如这个第五列第三行的这个女皇,这个皇后占领了这两个对角线, 如何来表示上对角线(皇后👸🏻的左上和右下从右上脚开始1、2、3、4、5...)?d1[n-col+7]=0;(col为女皇所在的列)也就是让行减去列再加上7就是表示的上对角线的个数。watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYzByZQ==,size_19,color_FFFFFF,t_70,g_se,x_16

有小朋友要问了:你咋知道的???给你看一个上对角线的图!!

用行-列得到的图是这样的(我的那个棋盘没有第零行,大家可以看成有第零行的和列的,这个图太难改了你懂我的意思吧):

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYzByZQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 而把这个数加7也就是行-列+7,是这样的:watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYzByZQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 这样我们不就可以用行标和列标来表示他所在的上对角线了嘛,神器吧!!

希望各位xdm都把这个背过说实话我要是自己推我也推不出来这个玩意!!

下对角线:用行加列就会神奇的发现变成了下面这个样子:watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYzByZQ==,size_20,color_FFFFFF,t_70,g_se,x_16


好了xdm现在让我们大声背三遍:

上对角线:行-列+7!!行-列+7!!行-列+7!!

下对角线:行+列!!行+列!!行+列!!


好了记住了吧!!!不要忘记了!像对待女朋友的生日一样对待这几个字!!!

这样我们就可以用:d1[n-col+7],d2[n+col]来读取皇后的对角线的占领情况了!

这个难点解决了,我们来进入下一个难点递归。


难点2:如何用递归的方法来放皇后?

1.皇后的放置问题:place[8]={0};来表示皇后的位置的话,用table[8][8]={0};来表示一个8*8的大棋盘用一个for循环来访问棋盘的每一行,如果这个位置可以放(那几个约束条件都是1没有被占领的话)我们就可以让这一行的皇后的位置棋盘上放上皇后即place[n]=col;,表示皇后在此列👸🏻(一会打印的时候可以用的到)然后让皇后的列和那两个对角线为0,表示占领! flag[col]=0; d1[n-col+7]=0;d2[n+col]=0;

2.皇后的递归调用问题:

我们想要一行一行的摆放皇后,然后一列一列的尝试对不对

用col来表示列的话,用一个循环:    for (col=0;col<8;col++)来访问每一列!

放置皇后用数组place[n]=col;(n表示行)来表示这个列,我皇后👸🏻占了!!!

flag[col]=0;  d1[n-col+7]=0;d2[n+col]=0;//将该皇后所在的行、列、对角线设置为被占领

这样我们皇后在一行的放置问题解决了!,那么如何访问每一行呢?

这时候我们就需要用到神奇的递归神器!

如果queen(n+1);发现,老资👸🏻没座位了!他就会对queen(n);说:“姐姐,姐姐你能不能去下一个座位让妹妹我坐下呀”如果queen(n);发现她如果向下面去她也没座位了她会向queen(n-1)说:....直到这8个皇后全坐下了,他们不吵架了,我们才能把这个棋盘输出出来!!!

这不就是老和尚给小和尚讲故事的递归嘛,而这个故事的大结局是皇后全都坐下!

我们如果这个行数小于七行(初始行为0你要是初始行为一可以加一)我们就可以继续向下摆放,对不对,就继续使用递归,来摆这个棋盘的下一行

if(n<7)    {queen(n+1);}//当行数小于7时;递归调用下一行。

如果我们的皇后把这次棋盘的摆好了我们就可以把这个棋盘输出出来了!

else{print();}(这个函数我们下面讲)//调用输出函数,

请大家看代码:

int queen(int n )//定义递归回溯函数
{int col;for (col=0;col<8;col++){if (flag[col]&&d1[n-col+7]&&d2[n+col])//判断皇后是否冲突{place[n]=col;//放置皇后flag[col]=0;d1[n-col+7]=0;d2[n+col]=0;//将该皇后所在的行、列、对角线设置为被占领if(n<7)	{queen(n+1);}//当行数小于7时;递归调用下一行else{print();}//调用输出函数flag[col]=1;//回溯d1[n-col+7]=1;d2[n+col]=1;}}return count;
}

输出了一个棋盘还不够,我们要输出所有的棋盘,怎么办呢??这时候我们就要请我们的回溯算法登场了! 


难点3:如何实现回溯?

因为我们用的是递归算法如果这一行输出不了我们就把责任退回给上一行,直到我们找到了一个棋盘上面正好能够放着这八个皇后的时候我们就把他打印出来了。逻辑是不是这样的

那么如果我们在循环的最后加上flag[col]=1; d1[n-col+7]=1; d2[n+col]=1;是不是就是把这个占领的皇后踢出去了,然后再把这个皇后放在下一列来看看后面的那些皇后能不能成功摆放?

什么时候会触发这个回溯算法呢??

答案是:当上面的递归算法全执行完了一次,并且输出了一个棋盘后,我们就可以让她执行第二次了!!

我们来继续讲一个故事:最后那个👸🏻在自己家里呆够了,老娘不想呆了,她又和楼上说,我要去下一个地方玩玩,你们看看怎么给我协调一下,原来我的位置就空出来了。楼上想;你去下面我去哪啊!于是他只好和楼上说,楼下和他说过的话。

这就是flag[col]=1; d1[n-col+7]=1; d2[n+col]=1;老娘不想待了,全给我搬家!

运行完这个以后呢,for循环中的col就进入下一列了,他这样一进不要紧,又触发了递归算法,一层一层的倒换,来满足需求。

当这个皇后全尝试了一遍其他能尝试的地方,就安稳了。

这样讲应该能讲明白为什么使用这个回溯算法,以及回溯算法为什么在if里面了吧如果不在if里面,这个回溯算法就没有意义了。

            flag[col]=1;//回溯d1[n-col+7]=1;d2[n+col]=1;


难点4:如何实现皇后位置的输出?

这个时候我们可以直接用一个输出函数来输出

这部分简单直接上代码:

void print()//定义输出函数
{int i,j;count++;//每调用一次输出函数number自加一次,记录摆放方法个数printf("No.%2d\n",count);int table[8][8]={0};//设置一个8*8的棋盘for (i=0;i<8;i++){table[i][place[i]]=1;//将每一行皇后所在位置赋值为1}for (i=0;i<8;i++){for (j=0;j<8;j++){printf("%d|",table[i][j]);}printf("\n");}
}

这样我们就能输出这些傲娇的皇后们了!!


全部代码如下:

#include<stdio.h>
int place[8]={0};//皇后位置
int flag[8]={1,1,1,1,1,1,1,1};//定义列
int d1[15]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};//定义上对角线(共有15个对角线,
//因此定义一个长度为15的数组,初值为1代表该对角线没有被皇后占领,
//若被皇后占领则赋值为0
int d2[15]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} ;//定义下对角线
int count=0;//记录输出次数
void print()//定义输出函数
{int i,j;count++;//每调用一次输出函数number自加一次,记录摆放方法个数printf("No.%2d\n",count);int table[8][8]={0};//设置一个8*8的棋盘for (i=0;i<8;i++){table[i][place[i]]=1;//将每一行皇后所在位置赋值为1}for (i=0;i<8;i++){for (j=0;j<8;j++){printf("%d|",table[i][j]);}printf("\n");}
}
int queen(int n)//定义递归回溯函数
{int col;for (col=0;col<8;col++){if (flag[col]&&d1[n-col+7]&&d2[n+col])//判断皇后是否冲突{place[n]=col;//放置皇后flag[col]=0;d1[n-col+7]=0;d2[n+col]=0;//将该皇后所在的行、列、对角线设置为被占领if(n<7)	{queen(n+1);}//当行数小于7时;递归调用下一行else{print();}//调用输出函数flag[col]=1;//回溯d1[n-col+7]=1;d2[n+col]=1;}}return count;
}
int main()
{
count=queen(0);//从第0行开始摆放皇后
printf("共有%d种方法",count);//输出摆放皇后的方法个数return 0;}

运行结果部分如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYzByZQ==,size_7,color_FFFFFF,t_70,g_se,x_16

可以看到一共有92种方法。


总结: 

递归问题就是推责任问题,下边不行就推给上边,上边不行,就推给上上边。

而回溯就像一个追求完美主义的人,我这边成了,我还要试试另一种解法行不行,等我把所有的结果都试出来就完美了!

我们人呢,既不能追求完美,也不能推卸责任,只有做好自己。

大家仔细的把这个八皇后算法搞懂,就会发现原来递归和回溯这么简单,不要得过且过!!

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYzByZQ==,size_20,color_FFFFFF,t_70,g_se,x_16

Love is worth years.
热爱可抵岁月漫长。


http://chatgpt.dhexx.cn/article/MeffoWQt.shtml

相关文章

利用ngrok实现域名映射局域网ip

前言 相信很多开发者都有这样的需求&#xff0c;需要让外网访问你本地的服务器&#xff0c;方便调试本地代码&#xff0c;或者让别人体验到自己做的应用。那么这时&#xff0c;我们需要做的就是将我们本地的端口映射到一个外网的端口上&#xff0c;也就是内网穿透。常见的解决…

python调用手机摄像头,实现实时调用摄像头,需要你的电脑和手机在同一个局域网内

1、android手机上安装一款APP&#xff1a;IP摄像头&#xff0c;app的图片如上图 2.调用代码如下 import cv2cv2.namedWindow("camera", 1) # 开启ip摄像头 video "http://admin:admin10.0.0.32:8081/" # 此处后的ipv4 地址需要改为app提供的地址 cap c…

02、处于不同局域网下的Socket通信(网络部分理论知识)

目录 一、服务器 1、服务器的种类和功能 2、服务器的操作系统 3、IIs、Apache、Tomcat 4、云服务器 弹性云服务器&#xff08;Elastic Cloud Server&#xff0c;ECS&#xff09; 云服务器安全组 二、OSI七层模型与TCP/IP五层模型 三、外网、内网、公网、私网 内网穿透…

使用wireshark抓取聊天信息(局域网内的udp通信)

文章目录 1&#xff0c;实验目的2&#xff0c;实验操作3&#xff0c;总结4&#xff0c;附件 1&#xff0c;实验目的 1.分析这程序所采用的是udp还是tcp 2.在抓取包中找到窃取到的聊天信息 (英文字符和汉字可能经过了某种编码转换&#xff0c;数据包中不是明文) 3.如果是网络连…

安装黑群晖找不到局域网电脑_黑群晖洗白太复杂?我用蒲公英P5轻松实现

前言: 随着网盘时代的结束,剩下的网盘供应商又开启了垄断方式,所以越来越多的小伙伴开始自己组自己的家庭NAS网络存储服务器。比如笔者的一个好基友就是如此。其实开始笔者是想让他直接一步到位,买群晖或者铁威马的NAS,在放入硬盘就可“一劳永逸”。然而,这个小伙伴看到了…

内网穿透实现局域网内搭建私服务器

使用云服务器实现内网穿透。内网里建立一台老旧win机专门用来挂pt&#xff0c;在上面存储视频和软件&#xff0c;而后映射在外网中&#xff0c;通过手机和电脑随时随地的下载和在线观看win机上的视频和文件。 1、修改ssh的默认端口 在公网中使用常用软件的默认端口会导致自己的…

局域网终结者_p2p终结者怎么安装使用 p2p终结者安装使用方法【介绍】

p2p终结者是一款局域网控制软件&#xff0c;他的主要功能就是控制和限制同一个局域网内其它的上网用户&#xff0c;如限制不让别人上QQ&#xff0c;不让别人开网页和不让别人下载&#xff0c;只要他和你在同一网之内你就可以控制他&#xff0c;并且神奇的是&#xff0c;不需要动…

Kali Linux-MSF远控局域网手机

前言 严正声明&#xff1a;本文仅限于技术讨论与分享&#xff0c;严禁用于非法途径。 本文目的&#xff1a;演示如何借助Kali Linux系统的Metasploit渗透测试框架生成远程控制程序&#xff0c;然后感染局域网内的Android手机&#xff0c;从而实现对目标手机数据的读取、音频的…

超级眼局域网监控软件 员工禁止软件 可以控制时间段

第一步&#xff1a; 下载超级眼局域网监控软件&#xff1a; 将经理端安装在管理者的电脑上&#xff0c;员工端安装在需要被监控者的电脑上。 第二步&#xff1a;在经理端电脑上操作。打开管理端软件&#xff0c;扫描所有的被控电脑。然后选择需要管理的电脑&#xff08;可以全选…

Kali使用Netdiscover探测局域网中存活主机

1、netdiscover介绍 Netdiscover 是一个主动/被动的ARP 侦查工具。使用Netdiscover工具可以在网络上扫描IP地址,检查在线主机或搜索为它们发送的ARP请求。 2、 主动模式:主动模式顾名思义就是主动的探测发现网络内主机,但是这种方式往往会引起网络管理员的注意。 打开Kali终…

如何避免计算机被别人共享,win7如何防止别人偷窥电脑 win7防止别人偷窥电脑操作方法...

我们都知道在win7系统中&#xff0c;可以设定开机密码或者屏保密码来防止我们的电脑被别人使用&#xff0c;不仅win7系统可以设置开机密码或者屏保密码&#xff0c;其他版本的系统也是可以设置的&#xff0c;那么win7如何防止别人偷窥电脑呢?下面为大家介绍win7防止别人偷窥电…

【时间之外】监控你的电脑只需8步

最近某安全厂商很火&#xff0c;很多离职员工都是被这套系统抓出来的。其实原理很简单&#xff0c;本文就用很多人都用过的kali系统&#xff0c;告诉你&#xff0c;想要控制你的电脑有多简单&#xff01; 网上的文章很多&#xff0c;如这篇&#xff0c;Kali攻防&#xff0c;写…

详细介绍别人电脑访问到自己电脑运行的项目

文章目录 让别人远程访问你的代码网站项目或临时演示你的项目给客户的方式详解 引言一、创建一个你想要别人访问的项目二、明确你想要将这个网站或者项目存放的地方 终端分类服务器设备WEB服务器三、部署我们的网页 本地部署流程进入浏览器输入网址访问获取本机的IP地址&#…

监控手机屏幕、监控电脑屏幕方案

一&#xff1a;手机投屏 实时监控多个手机屏幕&#xff08;<3台&#xff09; ApowerMirror软件&#xff0c;终生179&#xff0c;可同时连接3部手机 网络环境&#xff1a;电脑与测试手机需要在同一个WiFi网络环境下。(要求手机型号不同) 软硬件环境&#xff1a;电脑和手机分别…

基于VC++的局域网内主机监控系统设计与实现

局域网内主机监控系统 目录 引言 2 1.1 编写目的 2 1.2 项目背景 2 1.3 课题内容和要求 3 1.4 名词解释 4 参考文献 4需求分析 5 2.1 现有系统概述 5 2.2 对新系统的要求 5 2.3 系统要求 6 2.4 对功能的规定 6 2.5 对性能的规定 6 2.6 运行环境规定 7 2.6.2 软件配置 7系统架…

同一个局域网之内,如何远程控制对方的电脑而且不用对方同意

条件&#xff1a; 1、同一个局域网之内&#xff1b; 2、远程控制对方电脑&#xff1b; 3、不用对方做任何操作&#xff08;QQ远程控制需要对方点击同意才能进行远程&#xff09; 具体操作如下&#xff1a; 一、关闭对方电脑的防火墙 二.允许电脑被对方远程 计算机-属性-远…

详解LinkedList

目录 一、介绍 二、源码分析 1、LinkedList实现的接口 2、LinkedList中的变量 3、LinkedList的构造方法 &#xff08;1&#xff09;无参构造方法 &#xff08;2&#xff09;带集合参数的构造方法 4、LinkedList中的重要方法 &#xff08;1&#xff09;静态内部类Node …

java linkedlist 节点_JAVA学习-LinkedList详解

1.定义 实现List接口与Deque接口双向链表&#xff0c;实现了列表的所有操作&#xff0c;并且允许包括null值的所有元素&#xff0c;对于LinkedList定义我产生了如下疑问&#xff1a; 1.Deque接口是什么&#xff0c;定义了一个怎样的规范? 2.LinkedList是双向链表&#xff0c;其…

LinkedList入门教程

目录 LinkedList是什么&#xff1f;LinkedList的使用创建对象添加元素删除元素获取长度排序 常用方法 LinkedList是什么&#xff1f; LinkedList是一种数据结构&#xff0c;它由节点组成&#xff0c;每个节点包含一个值和一个指向下一个节点的指针。这种数据结构非常适合插入和…

Java--LinkedList

LinkedList的特点 LinkedList是基于双向链表实现的有序集合&#xff1b;LinkedList是非线程安全的&#xff1b;LinkedList中的元素可重复&#xff0c;可为null值&#xff1b;LinkedList可实现快速的插入和删除操作&#xff0c;与ArrayList相比&#xff0c;LinkedList的增删操作…