八皇后问题和n阶皇后问题的递归回溯解法

在这里插入图片描述

一、 问题:在8×8的国际象棋棋盘上摆放8个皇后,使其不能相互攻击(根据国际象棋的规则,皇后可以在上下、左右和对角线任意移动),即任意的两个皇后都不能处于同一行、同一列和同对角线,请问有多少种摆法并输出每一种摆法?

二、我们需要使用递归回溯的方法来解决。递归用于得到一种摆法,回溯用于得到所有可能的摆法。

问题中递归的体现:找到第n-1个皇后的位置与找到第n个皇后的位置的过程自相似,递归的终止条件为找到最后一个皇后的位置。

三、如何表示位置是否冲突或不冲突?

1、行(n):8个皇后,每个皇后占一行。

2、列(col):定义数组int place[8](place[0]~place[7])表示第n行皇后的列号,主要用于最后去输出结果。

还需要另一个数组表示列,目的是去表示该列的位置是否可以放皇后。定义数组bool flag[8],其中我们用true(1)表示不冲突,false(0)表示冲突。

3、对角线:我们需要两个数组去表示对角线,上对角线和下对角线。即bool d1[15] (d1[0]~d1[14]),和bool d2[15] (d2[0]~d2[14])。

在这里插入图片描述
在这里插入图片描述

那么我们应该如何去表示该上对角线和下对角线呢?

看上面两个图。

对于上对角线,我们用行号-列号后填充进8×8的棋盘中,可以发现,每一个上对角线内的值都是相等的,为了方便数组记录,我们将公式改为:行号-列号+7。

同样对于下对角线,我们用行号+列号后填充进8×8的棋盘中,可以发现,每一个下对角线内的值都是相等的,公式为:行号+列号。

这样,我们就可以表示上下对角线了。

四、程序思路

1.是否可以放置皇后:

flag[col] == 1 && d1[n-col+7] == 1 && d2[n+col] == 1,即所有的条件都为true。

2.放置皇后:

记录该皇后的位置:place[n] = col;(位置为(n , col),第n行第col列);
占领列:flag[col] = 0(false);
占领对角线:d1[n-col+7] = 0(false)和d2[n+col] = 0(false);

3.删除该皇后的位置(回溯,去寻找是否还有其他可以放置的位置):

列位置可以被占领:flag[col] = 1(true);
对角线位置可以被占领:d1[n-col+7] = 1(true)和d2[n+col] = 1(true);

五、程序:

#include <stdio.h>
#include <stdbool.h>

int place[8]={0};//记录皇后摆放的列号
bool flag[8]={1,1,1,1,1,1,1,1};//记录列是否被占领
bool d1[15]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,};//记录上对角线是否被占领
bool d2[15]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,};//记录下对角线是否被占领
int num=0;//记录摆放的方法总数

void print()//输出摆放方法
{
	int col, i, j;
	num++;//摆放方法+1
	printf("第%d种情况\n", num);
	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("%3d", table[i][j]);//输出棋盘
		}
		printf("\n");
		
	}
}

void generate(int n)//皇后的递归回溯函数
{
	int col;//表示列
	for(col=0;col<8;col++)//从第0列开始到第7列结束
	{
		if(flag[col]&&d1[n-col+7]&&d2[n+col])//判断该位置是否可以占领
		{
			place[n]=col;//记录皇后的位置
			flag[col]=false;
			d1[n-col+7]=false;
			d2[n+col]=false;
			//皇后占领后改变相应位置的状态为被占领
			if(n<7)//如果已经摆放的皇后数量未达到8个
			//递归出口(递归的终止条件)
			{
				generate(n+1);//递归,寻找下一行的皇后的位置
			}
			else//所有的8个皇后已经全部摆放完毕
			{
				print();//输出摆放方法
			}
			//回溯,拿走皇后后恢复相应位置为允许占领
			flag[col]=true;
			d1[n-col+7]=true;
			d2[n+col]=true;
		}
	}
}
//函数返回的两种情况:1、找到正确的摆法,print后,则回溯(重置棋盘),然后返回;
//2、对于当前的皇后,若循环了8次还是没有一个不冲突的位置可以摆放,则返回。
int main()
{
	generate(0);//从第0行开始
	printf("\n共有%d种摆放方法\n", num);
	return 0;
}
下面的视频可以展示整个程序运行的动态过程

可视化深搜八皇后问题_哔哩哔哩_bilibili

![[Pasted image 20231111185554.png]]

从上面的原理图中可以看出,回溯的过程发生在递归之后。

我们再来看一下具体的一部分过程:如果当n=7的时候,循环结束了还是没有找到不冲突的位置,则返回。那么根据递归的原理,这是一个出栈的过程,我们从void generate(7)返回到了上一个状态void generate(6)的if(n<7)的语句,接着我们继续执行函数generate(6),进入到回溯的语句,然后继续执行for循环或者是继续返回到generate(6)的上一个状态。

递归回溯算法通常先进行递归,然后在需要的时候进行回溯。在八皇后问题中:

1、递归:程序从第0行开始,尝试在每一列中放置一个皇后,如果在某一列放置的皇后不会导致冲突,则继续递归下一行的皇后
2、回溯:如果无法在当前行放置皇后,则程序会回溯到上一行,尝试在其他列去放置皇后。因此回溯指的是在搜索过程中,当无法继续前进时,程序会返回到之前的状态,去尝试其他可能的选择。

至此,我们解决了八皇后问题中的摆放方法的总数和输出每一种摆放方法。

下面的程序是解决 N 阶皇后的摆放问题:

#include <stdio.h>
#include <stdbool.h>
#define N 10

int place[N]={0};//记录皇后摆放的列号
bool flag[N]={0};//记录列是否被占领
bool d1[2*N-1]={0};//记录上对角线是否被占领
bool d2[2*N-1]={0};//记录下对角线是否被占领
int num=0;//记录摆放的方法总数

void print()//输出摆放方法
{
	int col, i, j;
	num++;//摆放方法+1
	printf("第%d种情况\n", num);
	int table[N][N]={0};//定义N×N的棋盘
	for(i=0;i<N;i++)
	{
		table[i][place[i]]=1;//1代表皇后在棋盘上的位置
	}
	for(i=0;i<N;i++)
	{
		for(j=0;j<N;j++)
		{
			printf("%3d", table[i][j]);//输出棋盘
		}
		printf("\n");
		
	}
}

void generate(int n)//皇后的递归回溯函数
{
	int col;//表示列
	for(col=0;col<N;col++)//从第0列开始到第7列结束
	{
		if(!flag[col]&&!d1[n-col+N-1]&&!d2[n+col])//判断该位置是否可以占领
		{
			place[n]=col;//记录皇后的位置
			flag[col]=true;
			d1[n-col+N-1]=true;
			d2[n+col]=true;
			//皇后占领后改变相应位置的状态为被占领
			if(n<(N-1))//如果已经摆放的皇后数量未达到N个
			//递归出口(递归的终止条件)
			{
				generate(n+1);//递归,寻找下一行的皇后的位置
			}
			else//所有的N个皇后已经全部摆放完毕
			{
				print();//输出摆放方法
			}
			//回溯,拿走皇后后恢复相应位置为允许占领
			flag[col]=false;
			d1[n-col+N-1]=false;
			d2[n+col]=false;
		}
	}
}
//函数返回的两种情况:1、找到正确的摆法,print后,则回溯(重置棋盘),然后返回;
//2、对于当前的皇后,若循环了N次还是没有一个不冲突的位置可以摆放,则返回。
int main()
{
	generate(0);//从第0行开始
	printf("\n共有%d种摆放方法\n", num);
	return 0;
}
如何去查看所有的结果?
先输入Windows+R,然后输入cmd,然后定位程序所在文件夹,如D:,输入dir,显示文件目录,再输入文件名,输入>result.txt,这样我们就将程序的结果保存在了名为result的txt文件中,我们可以前去文件夹D中查看。

在这里插入图片描述![在这里插入图片描述](https://img-blog.csdnimg.cn/0708d20ff6294714b398409ca5a134d0.png

在这里插入图片描述在这里插入图片描述

参考:懒猫老师-C语言-递归函数-八皇后问题(搜索,回溯)_哔哩哔哩_bilibili
可视化深搜八皇后问题_哔哩哔哩_bilibili
递归的一些基本的理解(个人)-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值