07 八皇后问题

来源:互联网 发布:mssql语句转mysql 编辑:程序博客网 时间:2024/05/17 06:35

前言

问题描述

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。
                                                                                                    – 来自百度百科

思路

思路 : 此处的实现为递归实现, 使用一个boolean[] 来维护当前皇后可落子的位置, 然后进行穷举各个可用位置, 并递归下一个皇后的可落子位置, 当遍历深度为maxDepth[皇后的个数]的时候, 打印结果

参考代码

/** * file name : Test12EightQueue.java * created at : 10:33:20 AM Jun 8, 2015 * created by 970655147 */package com.hx.test05;public class Test12EightQueue {    // 八皇后问题    public static void main(String args[]){      // 1. //      Queen8 queen = new Queen8();//      //核心函数//      queen.getArrangement(0);//      System.out.print("\r\n");//      System.out.println(queen.MAXQUEEN+"皇后问题有"+queen.num+"种摆放方法。");        // 2.        eightQueen();    }    // 八皇后问题, 初始化queenNum, queenPos, isRowAvail, planNum    private static void eightQueen() {        int queenNum = 8;        int[] queenPos = new int[queenNum];        boolean[] isRowAvail = new boolean[queenNum];        planNum = 0;        getForN(queenPos, 0, queenNum, isRowAvail);//      getForN02(queenPos, 0, queenNum);    }    // 获取第depth个王后的位置        // updateRowAvail更新第depth个王后可能的位置, 然后设置王后可能的位置         // 如果不行的话  回溯   再次设置    static int planNum = 0;    private static void getForN(int[] queenPos, int depth, int maxDepth, boolean[] isRowAvail) {        if(depth == maxDepth) {            planNum ++;            printResult(queenPos, planNum);            return ;        }        // 待优化        for(int i=0; i<maxDepth; i++) {            updateRowAvail(queenPos, depth, maxDepth, isRowAvail);            if(isRowAvail[i] ) {                queenPos[depth] = i;                getForN(queenPos, depth+1, maxDepth, isRowAvail);            }        }    }    // 思路和上面基本一致, 这里是以空间换时间, 降低了时间开销    private static void getForN02(int[] queenPos, int depth, int maxDepth) {        if(depth == maxDepth) {            planNum ++;            printResult(queenPos, planNum);            return ;        }        boolean[] isRowAvail = new boolean[queenPos.length];        updateRowAvail(queenPos, depth, maxDepth, isRowAvail);        for(int i=0; i<maxDepth; i++) {            if(isRowAvail[i] ) {                queenPos[depth] = i;                getForN02(queenPos, depth+1, maxDepth);            }        }    }    // 根据位置关系, 打印出王后的摆放位置    public static void printResult(int[] queenPos, int num){           int max = queenPos.length;           Log.log("第 " + num + " 种走法 : ");           for(int i=0;i<max;i++){             for(int j=0;j<max;j++){               if(i == queenPos[j]){                   Log.logWithoutLn("o ");               }else                   Log.logWithoutLn("x ");             }             Log.enter();           }        }    // 更新isRowAvail  检查前面的已经确定的位置, 设置下一列的可行性        // 首先 设置isRowAvail所有的元素为true        // 接着  遍历queenPos中depth之前的元素   更新isRowAvail[将不可落子的地方设置为false]    private static void updateRowAvail(int[] queenPos, int depth, int maxDepth, boolean[] isRowAvail) {        setAllTure(isRowAvail);        for(int i=0; i<depth; i++) {            isRowAvail[queenPos[i]] = false;            int off = depth - i;            if((queenPos[i] + off) < maxDepth) {                isRowAvail[queenPos[i] + off] = false;            }            if((queenPos[i] - off) >= 0) {                isRowAvail[queenPos[i] - off] = false;            }        }    }    // 设置该boolean[] 所有的元素为true    public static void setAllTure(boolean []isRowAvail) {        for(int i=0; i<isRowAvail.length; i++) {            isRowAvail[i] = true;        }    }    // 八皇后问题, 来自 : http://blog.csdn.net/zhong317/article/details/4586131    static class Queen8 {        public static int num = 0; //累计方案总数        public static final int MAXQUEEN = 8;//皇后个数,同时也是棋盘行列总数        public static int[] cols = new int[MAXQUEEN]; //定义cols数组,表示8列棋子摆放情况        public Queen8() {        }        public void  getArrangement(int n){             //遍历该列所有不合法的行,并用rows数组记录,不合法记为rows[i]=true             boolean[] rows = new boolean[MAXQUEEN];             for(int i=0;i<n;i++){                rows[cols[i]]=true;                int d = n-i;                if(cols[i]-d >= 0) {                    rows[cols[i]-d]=true;                }                if(cols[i]+d <= MAXQUEEN-1) {                    rows[cols[i]+d]=true;                 }             }             for(int i=0;i<MAXQUEEN;i++){               //判断该行是否合法                 if(rows[i])  continue;               //设置当前列合法棋子所在行数               cols[n] = i;               //当前列不为最后一列时               if(n<MAXQUEEN-1){                 getArrangement(n+1);               }else{                //累计方案个数                 num++;                 //打印棋盘信息                 printChessBoard();               }              }        }        public void printChessBoard() {           System.out.print("第"+num+"种走法 \r\n");           for(int i=0;i<MAXQUEEN;i++){             for(int j=0;j<MAXQUEEN;j++){               if(i==cols[j]){                 System.out.print("0 ");               }else                 System.out.print("+ ");             }             System.out.print("\r\n");           }        }    }}

效果截图

这里写图片描述

总结

一个很经典的问题, 记得曾经我看一本C++参考书中就有这个算法, 还有一个映像比较深的是 蛇形数组

这里使用的是递归, 当然可以改写位循环结构, 不过 会臃肿一点

参考
http://blog.csdn.net/zhong317/article/details/4586131

注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!

0 0
原创粉丝点击