题目:
利用栈结构实现八皇后问题。
八皇后问题19世纪著名的数学家高斯于1850年提出的。他的问题是:在8*8的棋盘上放置8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列、同一斜线上。请设计算法打印所有可能的摆放方法。
提示:
1、可以使用递归或非递归两种方法实现
2、实现一个关键算法:判断任意两个皇后是否在同一行、同一列和同一斜线上
需求分析:
(1) 输入的形式:为整形,且为大于3的整数。
(2) 输出的形式:为表格,程序结果打印的是在输入值(n)的大小基础上所有可能摆放的n个皇后的棋盘形式。
(3) 程序所能达到的功能:对所有皇后在n*n的棋盘上的摆放可能性进行分析,并最终输出达到题目要求的(即任意两个皇后不能相互攻击)所有棋盘。
(4) 测试数据:正确输入:4
正确输出:
错误输入:3
此情况下无输出
详细设计:
Main:
(1)定义整形数n和指针x,并使指针为一个大小为n+1的数组
(2)输出提示信息并从键盘上获取输入的整形数大小
(3)调用类Queen,调用其中的nQueens函数
(4)完成程序(return 0);
类函数Queen:
(1)在大括号中写上数据或函数的继承关系(public/private/protected)
(2)写出自定义的函数名或数据名,并在前面标注他们的数据(或返回数据)类型
(3)针对函数,在函数名后添上小括号并写出所需的参数及其类型
Check:
(1)根据放置条件,在循环中排查第k行第k列之前的k-1列是否可以放置皇后
(2)可以放置:返回1;不能放置:返回0
printQueen:
(1)第一重循环i从1~n;
(2)第二重循环j从1~n;
(3)当j等于x[i],即对应位置可以摆放皇后时,输出“Q”,否则输出“*”;
(4)每重循环结束一次都要输出一个换行符,达到输出一个表格的目的。
nQueens:
(1)定义一个整形数k,并使其初始值为1,代表从第一行开始考虑,k表示行数;
(2)将x[k]置为0,方便进入循环后自增为1;
(3)编写一个while循环,控制条件为k>0,因为当所有解都尝试完成后k将被置为0;
(4)x[k]自增一次,移入下一列;
(5)编写一个while循环,控制条件为x[k]<=n&&!check(x,k),表示不能在这个位置放置皇后,应移入下一列,x[k]++;
(6)在x[k]<=n的前提下,找到一个位置可以摆放皇后:
(6.1)k==n:已经查找到最后一行,是一个完整的解,直接调用printQueen输出;
(6.2)k<n:还未查找到最后一行,不是完整的解,移入下一行继续查找。
(7)x[k]>n:表示本行不能在上一行皇后位置的基础上摆放皇后,此时回溯到上一行。
函数调用关系图:
main->nQueens->check、printQueen。
用户使用说明:
输入想要的棋盘大小(皇后数)并按回车。
主程序部分
/* 利用栈结构实现八皇后问题。
八皇后问题19世纪著名的数学家高斯于1850年提出的。他的问题是:在8*8的棋盘上放置8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列、同一斜线上。请设计算法打印所有可能的摆放方法。
提示:
1、可以使用递归或非递归两种方法实现
2、实现一个关键算法:判断任意两个皇后是否在同一行、同一列和同一斜线上
2018.04.15 created by Cheng Zixin
*/
#include<iostream>
#include<math.h>
using namespace std;
//C++中,fabs函数用于求一个浮点数的绝对值
class Queen
{
public:
int check(int *x, int k); //用于判断是否在第k行第x[k]列进行放置
void printQueen(int *x, int n); //打印可能的放置方式
void nQueens(int *x, int n); //在n*n的棋盘上放置n个皇后使其不能相互攻击
private:
int n;
int *x;
};
int Queen::check(int *x, int k)
{
for (int i = 1; i < k; i++)
{
if (x[i] == x[k] || fabs(i - k) == fabs(x[i] - x[k]))
return 0; //表示不能正确放置下一个皇后
}
return 1; //可以正确放置皇后
};
void Queen::nQueens(int *x, int n)
{
int k = 1; //从第一行开始考虑(k表示行数,x[k]表示第几列
x[k] = 0; //进入循环后将从第一列开始,因此在这里置为0
while (k > 0) //当所有解都尝试完后,k=0
{
x[k]++; //移到下一列
while (x[k] <= n && !check(x, k)) //不能在这个位置放置,在第k行的基础上向后移一列
x[k]++;
if (x[k] <= n) //找到一个位置可以摆放皇后
{
if (k == n) //已经查找到最后一行,是一个完整的解
printQueen(x, n);
else //还未查找到最后一行,不是完整的解,还要从下一行的第一列开始继续查找,并
{
k++; x[k] = 0;
}
}
else //即x[k]>n 的情况,表示不能摆放皇后,回溯到上一行
k--;
}
};
void Queen::printQueen(int *x, int n)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (j == x[i]) //对应位置能够摆放皇后
cout << "Q";
else cout << "*";
}
cout << endl;
}
cout << endl;
};
int main()
{
int n;
int *x;
x = (int *)malloc(sizeof(n + 1));
cout << "请输入皇后数量(即棋盘大小):" << endl;
cin >> n;
Queen mQ;
mQ.nQueens(x, n);
system("pause");
return 0;
}
经验及体会:
虽然本题在直观上是一个二维数组,但是由于每一行只有一个元素,因此可以把它一维数组化。且在逐行排查的过程中,k值单调递增且间隔为1,
因此可以看成一个栈:当找不到符合要求的栈顶元素时出栈,原先的上一个元素变为新的栈顶元素。
栈的结构只是给我们提供一种新的考虑问题的思路并帮助我们简化问题,根本还是在问题本身上,栈只是一种算法,不必生搬硬套书上的标准模板,反而增加难度。