1、引言
首先,我们来了解一下什么是皇后问题。下面我抄录一段百度百科上的解释说明:
八皇后问题最早是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出。之后陆续有数学家对其进行研究,其中包括高斯和康托,并且将其推广为更一般的n皇后摆放问题。八皇后问题的第一个解是在1850年由弗朗兹·诺克给出的。诺克也是首先将问题推广到更一般的n皇后摆放问题的人之一。1874年,S.冈德尔提出了一个通过行列式来求解的方法,这个方法后来又被J.W.L.格莱舍加以改进。
好,现在我们知道了皇后问题实际是一个非常古老的问题了,接下来,我们描述一下,皇后问题的具体情况:在国际象棋的棋盘上,我们在每一行或者每一列,放且仅放置一个皇后,在放置的过程中,我们需要保证,该位置的对角线上,行,列上都不能放置皇后,请问最后一共有多少种放置方法?
棋盘如下所示(以下以八皇后为例的一个解):
2、算法描述
根据我们对皇后问题的阐述,可以对这个游戏有一个清晰的流程把握,以下是流程描述(下面扩展为以N*N的矩阵描述):
1) 算法开始,清空棋盘,当前行设为第一行,当前列设为第一列
2) 在当前行,当前列的位置上判断是否满足条件(即保证经过这一点的行,列与斜线上都没有两个皇后),若不满足,跳到第4步
3) 在当前位置上满足条件的情形:
在当前位置放一个皇后,若当前行是最后一行,记录一个解;
若当前行不是最后一行,当前行设为下一行, 当前列设为当前行的第一个待测位置;
若当前行是最后一行,当前列不是最后一列,当前列设为下一列;
若当前行是最后一行,当前列是最后一列,回溯,即清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置;
以上返回到第2步
4) 在当前位置上不满足条件的情形:
若当前列不是最后一列,当前列设为下一列,返回到第2步;
若当前列是最后一列了,回溯,即,若当前行已经是第一行了,算法退出,否则,清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置,返回到第2步;
算法流程图示:
3、伪代码
根据上面的铺垫分析,我们大致可以得到这样一个伪代码:
Fun queen(int row){
if (n == row){ //如果已经找到结果,则打印结果
print_result();
}else {
for (k=0 to N) { //试探第row行每一个列
if (can_place(row, k) {
place(row, k); //放置皇后
queen(row + 1); //继续探测下一行
}
}
}
}
4、源代码
JAVA的源代码如下(这里以八皇后为例解答,只是打印答案,并无图形界面):
public class Queen {
// 同栏是否有皇后,1表示有
private int[] column;
// 右上至左下是否有皇后
private int[] rup;
// 左上至右下是否有皇后
private int[] lup;
// 解答
private int[] queen;
// 解答编号
private int num;
public Queen() {
column = new int[8+1];
rup = new int[2*8+1];
lup = new int[2*8+1];
for(int i = 1; i <= 8; i++)
column[i] = 1;
for(int i = 1; i <= 2*8; i++)
rup[i] = lup[i] = 1;
queen = new int[8+1];
}
public void backtrack(int i) {
if(i > 8) {
showAnswer();
}
else {
for(int j = 1; j <= 8; j++) {
if(column[j] == 1 &&
rup[i+j] == 1 &&
lup[i-j+8] == 1) {
queen[i] = j;
// 设定为占用
column[j] = rup[i+j] = lup[i-j+8] = 0;
backtrack(i+1);
column[j] = rup[i+j] = lup[i-j+8] = 1;
}
}
}
}
protected void showAnswer() {
num++;
System.out.println("\n解答 " + num);
for(int y = 1; y <= 8; y++) {
for(int x = 1; x <= 8; x++) {
if(queen[y] == x) {
System.out.print(" Q");
}
else {
System.out.print(" .");
}
}
System.out.println();
}
}
public static void main(String[] args) {
Queen queen = new Queen();
queen.backtrack(1);
}
}
5、总结
皇后问题,是程序算法中的一个经典问题,在数据结构中,也常常拿来举例说明。皇后问题与之前我们介绍过的汉诺塔问题一样,是递归算法的经典展现,在皇后问题中,同时也使用到了程序设计中的经典思想----回溯。
对于这样经典的,写入教科书的案例,读者应该做到烂熟于心。笔者上大学的时候,老师是要求背下这些代码的,但是我个人不倾向于背下代码,一行不漏,但是,这样解决问题的思路,是非常有必要记下来的。
欢迎板砖,欢迎留言交流。