JAVA求解皇后问题


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、总结

皇后问题,是程序算法中的一个经典问题,在数据结构中,也常常拿来举例说明。皇后问题与之前我们介绍过的汉诺塔问题一样,是递归算法的经典展现,在皇后问题中,同时也使用到了程序设计中的经典思想----回溯。

对于这样经典的,写入教科书的案例,读者应该做到烂熟于心。笔者上大学的时候,老师是要求背下这些代码的,但是我个人不倾向于背下代码,一行不漏,但是,这样解决问题的思路,是非常有必要记下来的。

欢迎板砖,欢迎留言交流。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值