欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

八皇后问题

程序员文章站 2022-04-22 12:09:27
...

一、什么是八皇后问题?

八皇后问题是一个古老的问题,于1848年由一位国际象棋棋手提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,如何求解?

二、如何求解?

以高斯为代表的许多数学家先后研究过这个问题。后来,当计算机问世,通过计算机程序的运算可以轻松解出这个问题。

所谓递归回溯,本质上是一种枚举法。这种方法从棋盘的第一行开始尝试摆放第一个皇后,摆放成功后,递归一层,再遵循规则在棋盘第二行来摆放第二个皇后。如果当前位置无法摆放,则向右移动一格再次尝试,如果摆放成功,则继续递归一层,摆放第三个皇后…

如果某一层看遍了所有格子,都无法成功摆放,则回溯到上一个皇后,让上一个皇后右移一格,再进行递归。如果八个皇后都摆放完毕且符合规则,那么就得到了其中一种正确的解法。

说起来有些抽象,我们来看一看递归回溯的详细过程。

代码如下:

#include <iostream>
#include<math.h>
#include<typeinfo>
#include"delegate.h"
using namespace std;

#define  chessBoardSize  8
class Queen8
{
public:
	int(*chessBoard)[chessBoardSize];//int[8]类型的指针,
public:
	Queen8() 
	{
		 chessBoard = new int[chessBoardSize][chessBoardSize];
		 memset(chessBoard,0,sizeof(int)*chessBoardSize*chessBoardSize);
	}
	//在第几行落子,因为一行肯定只有一个,x默认值为0
	bool settleQueue(int x =0)
	{
		//放下了8个棋子才返回
		if (x == chessBoardSize)
			return true;

		//遍历当前行的所有列
		for (int y = 0;y<chessBoardSize;++y)
		{
			//清空这一行的数据以免干扰,因为走到头失败的话倒回来,之前的位置也会设置为1
			for (int i = 0; i < chessBoardSize; ++i)
			{
				chessBoard[x][i] = 0;
			}
			if (checkBoard(x,y))
			{
				chessBoard[x][y] = 1;
				if (settleQueue(x+1))
				{
						return true;
				}
			}
		}
		return false;
	}
	//检查落子符合要求
	bool checkBoard(int x,int y)
	{
		int i = 0;
		//检查纵向
		while (x-1-i>=0)
		{
			if (chessBoard[x - 1 - i][y] == 1)
				return false;
			++i;
		}
		//检查左斜上方块
		i = 0;
		while (x-1-i>=0&&y-1-i>=0)
		{
			if (chessBoard[x - 1 - i][y-1-i] == 1)
				return false;
			++i;
		}
		//检查右斜上方
		i = 0;
		while (x - 1 - i >= 0 && y + 1 + i < chessBoardSize)
		{
			if (chessBoard[x - 1 - i][y + 1 + i] == 1)
				return false;
			++i;
		}
		return true;
	}
};


int main()
{
	Queen8 q;
	q.settleQueue();
	for (int x =0;x<8;++x)
	{
		for (int y =0;y<8;++y)
		{
			cout << q.chessBoard[x][y] <<" ";
		}
		cout << endl;
	}
	system("pause");
	return 0;
	
}

上述代码的核心处在于settleQueue函数;

这个函数是在第几行落子,一直在循环中递归这个函数,放下8个棋子后会直接返回。

循环递归的原理如下

调用settleQueue------>第0行第0列------>清空这一行的数据------>检查这个位置------>可以放

------>继续调用settleQueue------>第1行第0列------>清空这一行数据------>检查这个位置------>不能放

------>for循环到第1行第1列------>清空这一行数据------>检查位置------>不能放------>继续for循环

…一直这样

(讲一个特殊情况)

------>放到了最后一行,最后一列,没有放慢8个棋子------>settleQueue返回false

------>返回上一行的settleQueue------>上一行的因为已经放下过一次棋子,所以清空一行的数据就很有必要------>然后for循环,棋子右移一个------>检查这个位置

------>继续调用settleQueue走到下一行

三、总结一下

就是循环递归每一行的每一列的位置。直至放下8个棋子返回true。

如果一行循环完了,棋子没有放下8个,就会返回false,递归倒回上一行进行for循环遍历

四、代码运行结果

八皇后问题