八皇后问题
一、什么是八皇后问题?
八皇后问题是一个古老的问题,于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循环遍历
四、代码运行结果
上一篇: 解析command字符串中有效命令
下一篇: 在OS X上安装git