LevOJ P1220皇后摆放问题(八皇后问题)

前言:

八皇后问题,甚至连我远房表弟都知道的计算机界经典问题,让我们看看怎么写吧

还是一样的,不想看分析的懒懒直接翻到最后抄代码就OK

思路

我们就题言题,这道题奶奶滴我在网上没找到一模一样的,找到的基本都是八皇后问题一共能有多少种解,真晦气。。咳咳,这道题,可能第一眼思路是用循环去遍历所有可能性,不过显然,这是一种非常非常不可行的方法,因为我们不知道第二个棋子的落子位置(其实吧试一试还是能试出来的,节目效果/doge)。说到这句话,有没有一点体会呢,我们不知道第二个棋子的位置,于是我们便应该将判断第二个棋子位置的任务就交给计算机了。

很好,假设我们已经让计算机判断成功第二个棋子的位置了,然后呢???

ANS:然后我们应该让计算机去尝试判断第三个棋子的位置

太对啦,我们应该让计算机去判断第三个棋子的位置,于是,大家觉不觉得这很像递归呢?实际上确实是这样,尽管我们知道这道题用的叫做回溯算法,可是其本质依旧是比较基础的递归。

让我们开始做题

首先初始化棋盘,我们没有必要使用8x8的数组来存储,因为经过我们聪明的脑瓜子思考,某一列有了一个棋子以后,这一列就不能放第二个棋子了!!get it!我们使用行、列、从右上到左下、从左上到右下四个数组来表示这个棋盘。全部初始化为0,表示全部无棋子。

已经落子的一列或者一行或者啥啥的,我们将数组值变为1

int x[9] = { 0 }; //行 用下标从1-8
int y[9] = { 0 }; //列 用下标从1-8
int leading_diagonal[16] = { 0 };//主对角线 用下标从1-15
int counter_diagonal[16] = { 0 };//副对角线 用下标从1-15

 构造递归函数,大体思路其实很简单,让我简单的说一说:

我们选择从第一行开始进行判断,具体细节我放代码注释里了

void put_queen(int n, int m)//n是已有的皇后数量,m是最大皇后数量
{
	//函数出口,当我们每一行都放上一个棋子,也就是8个棋子时, 结束函数,计数器+1 
	if (n == m) {
		++count;
		return;
	}
	//函数递归部分 
	else
	{
		for (int i = 1; i <= 8; i++)//从第一行开始,一行一行的遍历 
		{
			if (x[i] == 1)
				continue;//如果已经落子了,我们不予执行,继续判断下一行是否可行
			for (int j = 1; j <= 8; j++) //一列一列尝试 
			{
				//下面这个确定主(副)对角线方向的坐标其实是需要一定计算的,题主当时想了好久,找找规律也行叭 
				if (y[j] == 0 && leading_diagonal[i - j + 8] == 0 && counter_diagonal[i + j - 1] == 0)
				{
					//若满足落子条件,落子 
					x[i] = 1;
					y[j] = 1;
					leading_diagonal[i - j + 8] = 1;
					counter_diagonal[i + j - 1] = 1;
					put_queen(n + 1, m);//进入下一层,判断下一个棋子 
					//下面的就是回溯,出来以后将上一次落子的棋子取消掉,判断下一列 (这不是下一层奥,这边不懂的话重新理解理解递归) 
					x[i] = 0;
					y[j] = 0;
					leading_diagonal[i - j + 8] = 0;
					counter_diagonal[i + j - 1] = 0;
				}
				else//不能落子的话,尝试下一列落子 
					continue;
			}
			break;//这个break是必要的   
			/* 不然在你回溯到上一层,并且每一列都尝试过不行以后,我们希望的是回到上一行进行落子,如果没有这个break的话
			循环会忽略上一行的落子已经不行了这个事实,到下一行进行落子,于是便出现了错误 (终于想出来怎么讲了····)*/
			
		}
	}
	return;
}

这边的回溯出来其实就是回到了上一层落子,回溯的精髓

主函数的初始化数据啥的根据题目来的,没什么难度,整体代码放下头了

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int x[9] = { 0 }; //行 用下标从1-8
int y[9] = { 0 }; //列 用下标从1-8
int leading_diagonal[16] = { 0 };//主对角线 用下标从1-15
int counter_diagonal[16] = { 0 };//副对角线 用下标从1-15

int count = 0;//总可能性

void put_queen(int n, int m)//n是已有的皇后数量,m是最大皇后数量
{
	//函数出口,当我们每一行都放上一个棋子,也就是8个棋子时, 结束函数,计数器+1 
	if (n == m) {
		++count;
		return;
	}
	//函数递归部分 
	else
	{
		for (int i = 1; i <= 8; i++)//从第一行开始,一行一行的遍历 
		{
			if (x[i] == 1)
				continue;//如果已经落子了,我们不予执行,继续判断下一行是否可行
			for (int j = 1; j <= 8; j++) //一列一列尝试 
			{
				//下面这个确定主(副)对角线方向的坐标其实是需要一定计算的,题主当时想了好久,找找规律也行叭 
				if (y[j] == 0 && leading_diagonal[i - j + 8] == 0 && counter_diagonal[i + j - 1] == 0)
				{
					//若满足落子条件,落子 
					x[i] = 1;
					y[j] = 1;
					leading_diagonal[i - j + 8] = 1;
					counter_diagonal[i + j - 1] = 1;
					put_queen(n + 1, m);//进入下一层,判断下一个棋子 
					//下面的就是回溯,出来以后将上一次落子的棋子取消掉,判断下一列 (这不是下一层奥,这边不懂的话重新理解理解递归) 
					x[i] = 0;
					y[j] = 0;
					leading_diagonal[i - j + 8] = 0;
					counter_diagonal[i + j - 1] = 0;
				}
				else//不能落子的话,尝试下一列落子 
					continue;
			}
			break;//这个break是必要的   
			/* 不然在你回溯到上一层,并且每一列都尝试过不行以后,我们希望的是回到上一行进行落子,如果没有这个break的话
			循环会忽略上一行的落子已经不行了这个事实,到下一行进行落子,于是便出现了错误 (终于想出来怎么讲了····)*/
			
		}
	}
	return;
}

int main()
{
	int qipan[9][9] = { 0 };
	for (int i = 1; i <= 8; i++)
		for (int j = 1; j <= 8; j++)
			scanf("%d", &qipan[i][j]);

	int num_queen = 0;//我们这题初始棋子数字不知道,我们要用计数器记住,并传递到回溯函数中
	for (int i = 1; i <= 8; i++)
	{
		for (int j = 1; j <= 8; j++)
		{
			if (qipan[i][j] == 1)
			{
				++num_queen;
				x[i] = 1;
				y[j] = 1;
				leading_diagonal[i - j + 8] = 1;
				counter_diagonal[i + j - 1] = 1;
			}
		}
	}


	put_queen(num_queen, 8);
	printf("%d\n", count);

	return 0;
}

后记:

受益的懒懒们点个赞叭(鞠躬 

这边还有一道回溯的题目,相比之下八皇后这经典题目反而要难一点,链接放下头,也是OJ里的题目,有兴趣可以去看看

http://t.csdn.cn/NTW6A

  • 14
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值