题解——八皇后问题

八皇后问题

是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。

题意条件:
即在一个8*8的二维数字中选择8个不同行,不同列,不同正对角线,反对角线的位置。

题解思路:

  • 这里采用一种简单的递归的想法,即放一个便判断当前所放位置的合理性,如果不合理则进行回溯,返回上一步的状态。
  • 放置的方法可以考虑一整行一整行放置,这样做的好处是不用考虑同一行重复的情况。
  • 当一种方法找出来后,接下来寻找下一种方法,此时也应当回溯,并对下一个位置进行判断。
  • 由这种思路自然相当结束条件就是当每一行都找到第8个位置时退出递归。

题解步骤:

  • 设置数组并全置0(0表示此位置没有棋子,1表示此位置有棋子)
  • 可以先初步考虑递归函数的参数列表:数组的传递是必须的,因为是按行进行判断加点所以当前处理行的行数传递,可写做 void eight(int (*arr)[N],int n);
  • 因为行在参数列表中已经控制,所以函数内部直接控制列即可。
  • 在列内部,先对其赋值,然后去进行判断是否满足条件,满足就进入下一层递归,不满足就退出并对当前位置进行归0处理
  • 考虑打印条件,当处理完第8行时,下一次递归,可用作打印,即行下标为8时,打印
  • 打印完毕后返回上一层递归,此时此位置信息无用,归零(和不满足时处理一样,所以可以进行代码合并),循环进行下一个位置判断
  • 此时代码当每行都进行到第8列时,会不断回溯处理知道退出。
  • 再来考虑判断条件,实际上因为是按行处理,所以只需要判断此位置以上的行的信息和本位置信息的冲突。即判断此位置上半列,此位置左上对角线,右上对角线,即可。有冲突,直接返回0,满足条件返回1。
  • 至此,代码完成。
  • 将所有的8用define N来进行宏定义可实现N皇后的处理。

代码实例:
这里用C或C++语言是一样的。


#define N 8
int sum = 1;
void print(int(*arr)[N])
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			cout << arr[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;
}
//n:当前行	j:当前列
int panduan(int(*brr)[N], int n, int j)
{
	//判断其正上方
	for (int i = 0; i < n; i++)
	{
		if (brr[i][j] == 1)
			return 0;
	}
	//判断左上正对角线
	for (int i = 1; (n - i) >= 0 && (j - i) >= 0; i++)//行
	{
		if (brr[n-i][j-i] == 1)
		{
			return 0;
		}
	}
	//判断右上对象线
	for (int i = 1; (n - i) >= 0 && (j + i) < N; i++)
	{
		if (brr[n-i][j+i] == 1)
		{
			return 0;
		}
	}
	return 1;
}
//arr:存储数组	n:当前处理行数
void eight(int (*arr)[N],int n)
{
	//判断是否放满
	if (n == N)
	{
		cout << "第" << sum++ << "种" << endl;
		print(arr);
	}
	else//开始放入
	{
		for (int j = 0; j < N; j++)//列
		{
			arr[n][j] = 1;
			if (panduan(arr, n, j) == 1)
			{
				//当前行插入正确,进入下一行
				eight(arr, n + 1);
			}
			arr[n][j] = 0;
		}
	}
}

int main()
{
	int arr[N][N] = { 0 };
	//处理赋值
	eight(arr, 0);
	return 0;
}

结果显示:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值