八皇后问题——程序设计艺术与方法实验二 搜索算法的实现
八皇后问题: 在一个国际象棋棋盘上放八个皇后,使得任何两个皇后之间不相互攻击,求出所有的布棋方法。上机运行并检验结果。
分析:
国际象棋中关于皇后的可吃子范围的规则:
与皇后处在同一行中,同一列中,两个对角线方向的所有位子(格子)。
思路:
① 由规则可知,在棋盘的每行、每列及每条对角线上只能放置一个棋子方能满足上述条件。
② 因此,依次在每行(列)中试探着找一个合适的位置来放置棋子(此处不妨逐列试探),在能和已放置的棋子相容的前提下,继续其他列上位子的搜索。
③ 若本列没有合适的位置,则说明其前面的放置方法不合适,因而需要重新放置,即需要将原来所放的棋子去掉,再重新往下试探。
④ 去掉的次序按照最后试探的棋子最先去掉的原则进行(类似于图的深度遍历算法中的回溯)。
本题算法可用递归方式来描述。算法所需的参数如下:
(1)当前正在试探的列号(不妨用j表示);
(2)已经存放的各棋子;
(3)考虑到通用性, 将棋盘的行列数n作为一参数。
在实现时,需要注意以下问题的解决:
(1)如何表示棋盘中放置的棋子;
(2)某位置能放置一个棋子的条件的判断;
(3)前面的放置不行时,如何重新放置;
由于每行、列及对角线上只能有一个棋子,因而对每列来说,只需记录该列中的棋子所在的行号,因而用一维数组即可。如下面棋盘中的所放棋子及表示如下:
代码:
#include<iostream>
using namespace std;
/*八皇后问题:设计算法在国际象棋棋盘上放置八个皇后, 以使其中任意两个不能互相吃掉对方。
解:首先应了解一下国际象棋中关于皇后的可吃子范围的规则:与皇后处在同一行中,同一列中,两个对角线方向的所有位子(格子)。
问题分析:
由规则可知,在棋盘的每行、每列及每条对角线上只能放置一个棋子方能满足上述条件。
因此,本题可这样考虑:依次在每行(列)中试探着找一个合适的位置来放置棋子(此处不妨逐列试探),在能和已放置的棋子相容的前提下,继续其他列上位子的搜索。
若本列没有合适的位置,则说明其前面的放置方法不合适,因而需要重新放置,即需要将原来所放的棋子去掉,再重新往下试探。
去掉的次序按照最后试探的棋子最先去掉的原则进行(类似于图的深度遍历算法中的回溯)。
重复这一操作过程,直到所放棋子满足条件为止。
*/
int x = 1;
//(2)某位置能放置一个棋子的条件的判断
bool OK(int* a, int i1, int j1, int n) {//判断第j列的第i行能否放置
//初始化
int i = i1, j = j1; bool ok = true;
//检验前面j-1列是否选了第i行
while (j > 1 && ok) {
j--; ok = a[j] != i;
}
//重新回溯,检验主对角线上是否有元素
i = i1; j = j1;
while (j > 1 && i > 1 && ok) {
j--; i--; ok = a[j] != i;
}
//重新回溯,检验副对角线上是否有元素
i = i1; j = j1;
while (j > 1 && i < n && ok) {
j--; i++; ok = a[j] != i;
}
return ok;
}
void Print(int* a, int n) {
cout <<"第"<< x++ <<"种方法———"<< " 棋盘中每列中的棋子所在的行号:(";
for (int i = 1; i < n; i++)
cout << a[i] << " ";
cout << a[n] << ")";
cout << endl;
}
//按列开始遍历
void Queen(int* a, int j, int n) {
if (j > n)
Print(a, n);
else
for (int i = 1; i <= n; i++) {
if (OK(a, i, j, n)) {
a[j] = i;
Queen(a, j + 1, n);
}
}
}
int main() {
cout << "请输入棋盘规格(n行n列的n):";
int n;
cin >> n;
int* a = new int[n + 1];
cout << "皇后的位置有:" << endl;
Queen(a, 1, n);
return 0;
}
//思考:n也可以作为全局变量
运行结果: