八皇后问题(递归回溯算法详解+C代码)[通俗易懂]

八皇后问题(递归回溯算法详解+C代码)[通俗易懂]为了理解“递归回溯”的思想,我们不妨先将4位皇后打入冷宫,留下剩下的4位安排进4×4的格子中且不能互相打架,有多少种安排方法呢?现在我们把第一个皇后放在第一个格子,被涂黑的地方是不能放皇后的:第二行的皇后只能放在第三格或第四格,比如我们放在第三格:这样一来前面两位皇后已经把第三行全部锁死了,第三位皇后无论放在第三行的哪里都难逃被吃掉的厄运。于是在第一个皇后位于第一格,第二个皇后位于第三格…

大家好,又见面了,我是你们的朋友全栈君。

       为了理解“递归回溯”的思想,我们不妨先将4位皇后打入冷宫,留下剩下的4位安排进4×4的格子中且不能互相打架,有多少种安排方法呢?现在我们把第一个皇后放在第一个格子,被涂黑的地方是不能放皇后的:

12

       第二行的皇后只能放在第三格或第四格,比如我们放在第三格:

放第二个皇后
       这样一来前面两位皇后已经把第三行全部锁死了,第三位皇后无论放在第三行的哪里都难逃被吃掉的厄运。于是在第一个皇后位于第一格,第二个皇后位于第三格的情况下此问题无解。所以我们只能返回上一步,来给2号皇后换个位置:
给第二个皇后换位置
       此时,第三个皇后只有一个位置可选。当第三个皇后占据第三行蓝色空位时,第四行皇后无路可走,于是发生错误,则返回上层调整3号皇后,而3号皇后也别无可去,继续返回上层调整2号皇后,而2号皇后已然无路可去,则再继续返回上层调整1号皇后,于是1号皇后往后移一格位置如下,再继续往下安排:
回溯重新安排一号皇后
核心代码如下:

void EightQueen( int row )
{
	int col;
	if( row>7 )                       //如果遍历完八行都找到放置皇后的位置则打印
	{
		Print();                       //打印八皇后的解 
		count++;
		return ;
		
	}

	for( col=0; col < 8; col++ )        //回溯,递归
	{
		if( notDanger( row, col ) )    // 判断是否危险
		{
			chess[row][col]=1;
			EightQueen(row+1);
			
			chess[row][col]=0;           //清零,以免回溯时出现脏数据
		}
	}
}

我们来重点看一下这段代码:

       第一次进来,row=0,意思是要在第一行摆皇后,只要传进来的row参数不是8,表明还没出结果,就都不会走if里面的return,那么就进入到for循环里面,col从0开始,即第一列。此时第一行第一列肯定合乎要求(即notDanger方法肯定通过),能放下皇后,因为还没有任何其他皇后来干扰。

       关键是notDanger方法通过了之后,在if里面又会调用一下自己(即递归),row加了1,表示摆第二行的皇后了。第二行的皇后在走for循环的时候,分两种情况,第一种情况:for循环没走到头时就有通过notDanger方法的了,那么这样就顺理成章地往下走再调用一下自己(即再往下递归),row再加1(即摆第三行的皇后了,以此类推)。第二种情况:for循环走到头了都没有通过notDanger方法的,说明第二行根本一个皇后都摆不了,也触发不了递归,下面的第三行等等后面的就更不用提了,此时控制第一行皇后位置的for循环col加1,即第一行的皇后往后移一格,即摆在第一行第二列的位置上,然后再往下走,重复上述逻辑。

       注意,一定要添加清零的代码,它只有在皇后摆不下去的时候会执行清0的动作(避免脏数据干扰),如果皇后摆放很顺利的话从头到尾是不会走这个请0的动作的,因为已经提前走if里面的return方法结束了。

       总之,这段核心代码很绕,原理一定要想通,想个十几二十遍差不多就能理解其中的原理了,递归回溯的思想也就不言而喻了。八皇后问题一共有92种情况

完整代码如下:

#include <stdio.h>

int count = 0;
int chess[8][8]={0};

int notDanger( int row, int col )
{
	int i,k;
	// 判断列方向
	for( i=0; i < 8; i++ )
	{
		if( chess[i][col]==1 )
		{
			return 0;
		}
	}
	// 判断左对角线 
	for( i=row, k=col; i>=0 && k>=0; i--, k-- )
	{
		if(chess[i][k]==1  )
		{
			return 0;
		}
	}
	// 判断右对角线 
	for( i=row, k=col; i>=0 && k<8; i--, k++ )
	{
		if(chess[i][k]==1  )
		{
			return 0;
		}
	}
	return 1;
}

void Print()          //打印结果 
{
	int row,col;
	printf("第 %d 种\n", count+1);
		for( row=0; row < 8; row++ )
		{
			for( col=0; col< 8; col++ )
			{
				if(chess[row][col]==1)        //皇后用‘0’表示
				{
					printf("0 ");
				}
				else
				{
					printf("# ");
				}
			}
			printf("\n");
		}
		printf("\n");
}

void EightQueen( int row )
{
	int col;
	if( row>7 )                       //如果遍历完八行都找到放置皇后的位置则打印
	{
		Print();                       //打印八皇后的解 
		count++;
		return ;
		
	}

	for( col=0; col < 8; col++ )        //回溯,递归
	{
		if( notDanger( row, col ) )    // 判断是否危险
		{
			chess[row][col]=1;
			EightQueen(row+1);
			
			chess[row][col]=0;           //清零,以免回溯时出现脏数据
		}
	}
}

int main()
{
	EightQueen(0);        
	printf("总共有 %d 种解决方法!\n\n", count);
	return 0;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/147763.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月15日 上午9:00
下一篇 2022年6月15日 上午9:16


相关推荐

  • ALSA的pulse插件_pulseaudio启动

    ALSA的pulse插件_pulseaudio启动此博文为记录我初次进行树莓派开发语音唤醒时遇到的问题以及解决方法,如果有更好的方法,欢迎讨论。问题一描述:我在进行snowboy的安装过程中,所有的程序能够正常运行,也能正常录音以及音频输出,但是树莓派重启后,我运行之前设置好的程序,程序依然能够运行,但不能进行正常的唤醒。我查了/.asoundrc没有问题,录音设备和音频输出设备也是正常的状态,在系统中也能列出。问题一解决:经过多次重装系统(因为我的树莓派不只是只有做语音唤醒,我一开始也不知道是哪里的问题)的排除,发现是树莓派重启之后pulseau

    2022年10月16日
    4
  • OpenAI发布通用智能体ChatGPT Agent:实现自主思考、联网与工具调用,智能体赛道大变天!

    OpenAI发布通用智能体ChatGPT Agent:实现自主思考、联网与工具调用,智能体赛道大变天!

    2026年3月16日
    2
  • Java中获取文件大小的正确方法

    Java中获取文件大小的正确方法本文出处:http://blog.csdn.net/chaijunkun/article/details/22387305,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。今天写代码时需要实现获取文件大小的功能,目前有两种实现方法,一种是使用File的length()方法;另外一种是使用FileInputStream的available(

    2022年4月30日
    240
  • bs模型的通俗理解_白话

    bs模型的通俗理解_白话要想不用一个数学模型只用大白话说明白Black-Scholes这个伟大的期权类衍生品定价模型,似乎与用地球语言解释火星文化一样的困难。所以我的所谓白话也不可能是真的大白话了,总要摆出几个简单的数模以说明问题。只不过这些数学上的东西我相信有一点数学和统计学基础的朋友都能看的明白了。事实上即使摆出一大堆数学模型,我也没有能力真的写出其推导的全过程。幸好我的目的不是写清楚BS模型的推导,而是从其原理性的

    2022年4月19日
    46
  • 串口调试助手(CM野人版)4.0有严重Bug,已经跟作者反应,等候更新

    串口调试助手(CM野人版)4.0有严重Bug,已经跟作者反应,等候更新这个软件,看起来很漂亮,能任意设置波特率,支持921600波特率,看起来很华丽。但最近发现其一个严重的Bug:版本就是4.0我打印了一些信息:能正常显示。我把这些打印的代码行给注释了,依然能打印出来,见鬼了。然后我重启电脑,问题消失,不再打印注释过的代码行。我换了别的串口助手,问题消失。所以一定是这个助手的问题。基础的调试软件出问题,非常不易发现,带来的损失非常大。建

    2022年5月8日
    87
  • 【无标题】循环队列元素个数计算

    【无标题】循环队列元素个数计算某循环队列的存储空间为 Q 1 m 初始状态为 front rear m 现经过一系列的入队操作和退队操作后 front m rear m 1 则该循环队列中的元素个数为 C A 1B mC m 1D 0 设循环队列的存储空间为 Q 1 m 初始状态为空 在循环队列运转起来后 如果 rear front gt 0 则队列中的元素个数为 rear front 个 如果 rear front lt 0 则队列中的元素个数为 rear front m 个 即

    2026年3月18日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号