经典问题——八皇后问题:最适合C语言初学者的解法
什么是八皇后问题:
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。
深究过C语言或者其他语言的同学可能有很简单快速的算法,但是对于初学者来说,可以说这是一道比较难的算法题,因为许多算法我们都没有接触过,那对于C语言的初学者我们该怎么来解决这道题呢?接下来我们就一起来探究下解法。
思路分析
首先我们思考一下,我们是应该一个一个的放置皇后还是该先把八个皇后全部放入棋盘然后在进行移动操作?我们是应该一个一个的放置皇后的。所谓牵一发而动全身,当八个皇后全部放入棋盘后,我们每移动一个皇后就会导致其他皇后的位置都得移动,从而导致了运算过程的冗杂重复,也导致了计算时间的增加和内存的浪费,对于追求效率的C语言来说这是不可取的,所以我们选择一个一个的放置皇后。
这样操作有什么优点呢?每当我们在选择移动一个皇后的位置的时候,其它已经放置好了的皇后的位置是不需要移动的,如果当某个皇后在属于它的行都不满足条件时,我们才需要回溯过去改变上一个皇后的位置。我们需要的就是循环的进行回溯操作,直到所有的皇后都放置完成,这样计算机的每一步操作都是有用的,不再是无用功。
我们来看一下回溯法:回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
下面我们一起来看一下八皇后问题的代码实现过程
PS:因为这篇文章针对初学者,所以我们就不用函数来写了,直接在main函数里面实现。
首先是定义声明阶段:
这是一个8*8的棋盘,可能许多同学认为应该定义一个二维数组来保存这个棋盘,但是完全没有必要。
在这里我们只需要定义一个一维数组就完全够用了。为什么呢?因为皇后的攻击力太强,导致同行的皇后都会被攻击,那么一行只能放一个皇后,我们用这个一位数组的值就可以表示这个皇后所在的列了。
声明定义阶段:
int queen[8] = {0}; //用来储存皇后的位置 即queen的值就为第i行的列//queen[0]表示第0行//queen[i]表示第i行int cnt = 0; //表示摆放了几个皇后,也表示摆放皇后的行数。int col = 0; //表示在这一列上摆放了皇后int sum = 0; //总共有几种摆法
在定义好之后我们就可以一个一个的放置皇后了
因为我们并不知道总共需要循环多少次,所以我们用一个while(1)的循环来放置皇后。
首先我们要清楚皇后所在的位置:(cnt,rol),在上面的定义阶段我们可以看到用cnt来表示共摆放了几个皇后,用col来表示在这一列上摆放皇后,那当前正在摆放的皇后的坐标不就是**(cnt,col)**吗?
皇后摆放阶段:
首先我们需要判断的是当前这个皇后能否被其他已经放置好了的皇后所攻击到,如果可以被攻击到,那么这个地盘就不属于这个皇后,如果不会被攻击,那大家就相安无事,都安顿下来不要再打打杀杀。
所以我们需要定义一个标志变量isAttack,如果它为1,表示它会被其它皇后所攻击,如果为0则不会被其他皇后攻击。
int isAttack = 0;
然后我们再来判断哪些情况下这个可怜的皇后会被其他同僚所毒杀:
**当然他们不会再同一行上,只可能是在同一列或者同一斜线上。**所以只需要两个if语句就可以把这两种情况都包含了。
for(i=0;i<cnt;i++){if(queen[i] == col){ //表示在同一列上isAttack = 1; } int div_row = cnt -i; //表示斜线上的纵坐标之差int div_col = queen[i]-col; //表示斜线上横坐标之差if(div_row == div_col ||div_row == -div_col){ //表示在同一斜线上isAttack = 1; }}
我来解释下代码:
当已经有cnt个皇后被安顿好了,那我们正在寻找安顿之所的楚楚可怜弱小又无助的皇后就得思前想后,和每个皇后都得避嫌,不能让这cnt个皇后抓到把柄,所以需要和每一个皇后都进行比较,如果在这个地方不能安顿,就得另寻住所。所以我们进行了cnt此循环。
前面我们说过,queen[i]它的值表示第i个皇后所在的列,所以我们要用一个if语句来排除同一列的情况,如果在同一列上就把isAttack这个标志位置1;
我们发现当两个皇后处于同一斜线时,她们的纵坐标之差和横坐标之差是相等或者为相反数的
比如我们发现Q1和Q2处于同一斜线上,那么她们必然不可能和平共处,此时Q1的坐标是(3.3),而Q2的坐标是(1,5),
此时3-1 = -(3-5),就能证明我们上面所说处于同一斜线上的皇后们她们的横坐标之差和纵坐标之差是相等或者相反的。
所以我们用第二个if语句来排除这种情况,如果处于同一斜线,标志位isAttack置1;
判断完能否被攻击到这个过程后,我们最后需要的是处理当皇后能被攻击或者不能被攻击时的情况。
当皇后不能被攻击即当标志位为0时:
那么很简单,就是把这个皇后安顿下来。记录当前皇后的列数,放置的皇后+1(就是这个皇后经历过重重考验后此刻也暂时变成了一个毒妇人),col置0,让下一个皇后从第0列开始寻找安家之所,重复上面的安家之旅。如果当八个皇后都安顿好之后那恭喜你,这就是一种解法了。只需要打印出来并且寻找下一种解法。我们来看一下代码:
if(isAttack == 0){ //表示可以放置queen[cnt] = col; //记录皇后当前的列数cnt++; //开始摆放下一个皇后col = 0; //下一个皇后从第一列开始遍历if(cnt == 8){ //如果摆满了八个皇后就打印出他们的摆法for(i=0;i<8;i++){printf("%d ",queen[i]+1); } printf("\n"); sum++; //并且摆放种数+1}}
我们该怎么寻找下一种解法了?那就是这题的核心了:回溯算法。
do{ //越界问题 //回溯cnt--; //撤回正在摆放的皇后col = queen[cnt]+1; //往下一个列寻找摆放位置
}while(col>=8);
首先cnt - - ;就是撤回这在摆放的皇后,让她去试一下下一列是否也能安家,如果刚好可以安家,恭喜你,这又是一种解法了,但是当皇后所在的列数超过八列时,我们就需要重新考虑上一个皇后的位置了。
所以我们这里用do{}while();语句,保证当八个皇后都安顿好之后至少撤去一个皇后,让她重新寻找安家之所。然后当皇后在这一行所有八个位置都试过了不能安家之后,即col>=8;就需要麻烦上一个皇后起驾换一个家,让后来的皇后有家可住。比如下图:
从上图我们可以看出,当上面七个皇后都相安无事安顿好了之后,我们却发现已然没了第八个皇后的容身之所,这是世界人民大团结所绝不能允许的,所以我们需要Q7这个皇后迁居另寻他所(就是Q7往下一列试),我们发现往下一列后Q7也没有了住处,所谓赠人玫瑰手有余香,当你Q7昔日帮助了Q8,今日我Q6忘记爱恨情仇也来助你Q7一臂之力,所以我Q6也一列一列的往后试,如此循环往复,直致所有的皇后们都在这个三千世界找到属于自己的一隅之地。
当标志位为1即当前皇后能被其他皇后攻击到时:
那我们就重复上面的操作,用回溯算法找帮这个皇后找其他安家之所。
else{ //表示不能摆放col++;while(col>=8){ //回朔cnt--; //退一格col = queen[cnt]+1; //上一个皇后往后移一格}
}
什么意思呢?就是让这个皇后的列数col+1,即往后遍历住所,当出现列数超过8的时候就和通过回溯寻找其他方案。
最后通过这些简单地操作计算机就能找出所有的结果了。
while(1)退出条件
我们在一次一次的找寻中,那要怎么样我们才算找到了所有的答案了呢?
这个问题也很简单,只需要一个if语句就搞定:
if(cnt == 1 && queen[0] == 7 && col == 6){ //表示第一行的皇后已经到了第八列且第二行的皇后到了第六列位置,已经摆放不下皇后了就退出循环break; }
什么意思呢?就是当第一行已经有了一个皇后,且这个皇后到了最后一列,而且第二个皇后在遍历到第七列的时候还没有找到合适的位置,那这个时候我们就break退出这个while循环。
如上图所示,此时第二个皇后已经不能往后遍历了,而且也已经不能回溯了(第一个皇后已经到底了),那此时我们就退出循环。
完整源代码
最后我们来完整的看一下解决八皇后问题的代码:
#include <stdio.h>int main(){int queen[8] = {0}; //用来储存皇后的位置 即queen的值就为第i行的列//queen[0]表示第0行//queen[i]表示第i行int cnt = 0; //表示摆放了几个皇后,也表示摆放皇后的行数。int col = 0; //表示在这一列上摆放了皇后int sum = 0; //总共有几种摆法while(1){//在(cnt,col)这个坐标摆放皇后if(cnt == 1 && queen[0] == 7 && col == 6){ //表示第一行的皇后已经到了第八列且第二行的皇后到了第六列位置,已经摆放不下皇后了就退出循环break; }int isAttack = 0; //用来表示皇后们之间是否能够攻击的到,如果攻击的到就是1,否则就为0int i=0;for(i=0;i<cnt;i++){if(queen[i] == col){ //表示在同一列上isAttack = 1; } int div_row = cnt -i; //表示斜线上的纵坐标之差int div_col = queen[i]-col; //表示斜线上横坐标之差if(div_row == div_col ||div_row == -div_col){ //表示在同一斜线上isAttack = 1; }}if(isAttack == 0){ //表示可以放置queen[cnt] = col; //记录皇后当前的列数cnt++; //开始摆放下一个皇后col = 0; //下一个皇后从第一列开始遍历if(cnt == 8){ //如果摆满了八个皇后就打印出他们的摆法for(i=0;i<8;i++){printf("%d ",queen[i]+1); } printf("\n"); sum++; //并且摆放种数+1do{ //越界问题 //回朔cnt--; //撤回正在摆放的皇后col = queen[cnt]+1; //往下一个列寻找摆放位置}while(col>=8); }}else{ //表示不能摆放col++;while(col>=8){ //回朔cnt--; //退一格col = queen[cnt]+1; //上一个皇后往后移一格}}}printf("总共有%d种摆法\n",sum);return 0;
}
结果:
完美解决!!
用最简单的代码实现最复杂的问题,C语言并不是很难。
经典问题——八皇后问题:最适合C语言初学者的解法相关推荐
- Java黑皮书课后题第7章:***7.36(游戏:八皇后问题)经典的八皇后难题是要将八个皇后放在棋盘上,任何两个皇后都不能相互攻击(没有两个皇后在同行、同列、同一对角线)。编写程序显示一个解决方案
7.36(游戏:八皇后问题)经典的八皇后难题是要将八个皇后放在棋盘上,任何两个皇后都不能相互攻击(没有两个皇后在同行.同列.同一对角线).编写程序显示一个解决方案 题目 题目描述 破题 题目 题目描述 ...
- 2021-01-11经典的八皇后问题和N皇后问题, 回溯
八皇后的来源 八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行.纵行或斜线 ...
- 八皇后问题初始思路python_【单人解谜】经典的八皇后问题解析
哈啰~ 大家好,今天分享一个有趣的皇后问题. 八皇后问题是西洋棋延伸而来的问题, 西洋棋的皇后是威力很大的棋子, 皇后在棋盘上的「攻击范围」为同行.同列.同对角线的地方, 要如何在8*8的方形棋盘上摆 ...
- 洛谷P1219 [USACO1.5]八皇后 Checker Challenge(C语言)
洛谷P1219 [USACO1.5]八皇后 Checker Challenge 题目 代码 这个八皇后问题也是经典入门深搜的题目了呀,理论上深搜都可以运用数据结构的栈来模拟计算机内部的压栈操作.但这里 ...
- 用java实现八皇后问题_使用java语言实现八皇后问题
八皇后问题,在一个8X8的棋盘中,放置八个棋子,每个棋子的上下左右,左上左下,右上右下方向上不得有其他棋子.正确答案为92中,接下来用java语言实现. 解: package eightQuen; / ...
- JAVA用爬山法解决八皇后问题_八皇后问题爬山法实现(C语言)
1 #include 2 #include 3 #include 4 #include 5 // 6 //编程题7 //爬山法(八皇后问题)8 // 9 10 11 //棋子结构体12 //typed ...
- 回溯法(八皇后问题)及C语言实现
回溯法,又被称为"试探法".解决问题时,每进行一步,都是抱着试试看的态度,如果发现当前选择并不是最好的,或者这么走下去肯定达不到目标,立刻做回退操作重新选择.这种走不通就回退再走的 ...
- 八皇后问题4种c语言算法
八皇后问题 1.递归回溯法 B站懒猫老师讲的(我在这里学的) 八皇后问题的递归回溯算法思路:从第一行开始当某一行皇后位置不与前面所有皇后位置冲突那么记录该行皇后位置并调用递归函数进入下一行,摆放下一个 ...
- Python 八皇后问题(Eight queens)的简单解法
本解法的思路是先使用 np.zeros((8,8),dtype=int) 生成8*8的二维数组,每行放一个皇后,放上皇后的位置值为1,否则为0.使用8个for循环来依次对每一行的皇后进行控制,在循环的 ...
最新文章
- qq邮箱高频率邮件来源自动屏蔽的信任办法
- UVA - 1604Cubic Eight-Puzzle立体八数码
- IBASE view WD_DESTROY - ON_NEW_FOCUS
- 实现一个无法被继承的C++类
- c++ websocket 客户端
- crontab java job_crontab 定时任务
- javascript的笔记精简版
- oracle创建用户、创建表空间、授权、建表
- Java程序调用高德开放API——IP定位
- matlab里peaks,MATLAB中peaks函數的用法
- 流程效率低?这里有3个流程优化的建议
- Linux: ubuntu Appium连接手机
- iOS8新功能新特性
- 前端html网页,点击按钮或超链接 弹出 一个登陆的div窗口或者对话框
- 2019年 阿里巴巴Python 面试必备 !100 问
- 可编辑的el-table表格
- 什么是VR虚拟电子沙盘3D数字沙盘有什么功能
- 基于FPGA的VGA/LCD显示控制器设计(中)
- 小学计算机教资报名科目,教师资格证小学可以报考哪些科目?
- EYEOS WEB操作系统的安装方法