【短学期算法作业】八皇后问题(回溯法)

分类专栏:
用Java写数据结构

文章标签:
Java
原创

题目介绍

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际象棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋棋盘上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一对角线上,问有多少种摆法。高斯认为有76种方案。1854年柏林象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。 Input 无输入数据 Output 输出所有解

题目思路

根据规则我们可以得出每行必有一个皇后,则找指定皇后时只需在当行遍历。 首先根据规则找到一个皇后,然后根据规则把棋盘上不能有皇后点全部标记,再在剩下的棋盘上找下一个没被标记的皇后,如果找不到则回溯。 回溯:这个操作需要在每一步结束时加个“快照”,以便于回溯

具体代码

package com.dreamchaser;

import java.util.Stack;

/**
 * 八皇后问题
 * 每行都肯定有一个皇后
 */
public class test4 {
    //维护一个当前正在处理的矩阵
    static int[][] finalCheckerBoard=new int[8][8];
    static Stack<Step> stack=new Stack<Step>();
    /**
     * 控制循环的开关
     */
    static boolean flag=true;

    public static void main(String[] args) {
        /**
         * 第n组解
         */
        int n=0;
        //初始值
        Spot spot=findOne(finalCheckerBoard,stack.size()+1);
        while (flag){
            Step step=new Step();

            if (spot!=null){
                //若找到则将这个点处理后的棋盘与原来棋盘合并,然后入栈
                int[][] c;
                c=handle(spot);
                finalCheckerBoard=merge(c,finalCheckerBoard);

                //赋值给step,相当于给当前步骤存档,以便回溯
                step.setSpot(spot);
                step.setCheckerboard(finalCheckerBoard);
                step.setNumber(stack.size()+1);
                //步骤入栈
                stack.push(step);

                spot=findOne(finalCheckerBoard,stack.size()+1);


                if (stack.size()==8){
                    Stack<Step> steps= (Stack<Step>) stack.clone();
                    n++;
                    System.out.println("第"+n+"组解:");
                    while (steps.size()>0){
                        System.out.println(steps.pop().spot);
                    }
                    recall();
                }
            }else{
                //若没找到,则回溯
                spot=recall();
            }
        }

    }

    /**
     * 回溯,返回上一步的下一个点,如果没有下一个点则放回null
     * @return 上一步的下一个点
     */
    private static Spot recall(){
            Step step=stack.pop();
            Spot spot=step.getSpot();
            if (spot.getY()<7){
                //处理finalCheckerBoard
                if (stack.isEmpty()){
                    //如果堆栈是空的话,则赋值一个初始值

                    finalCheckerBoard=new int[8][8];
                    for (int j=0;j<= spot.getY();j++){
                        finalCheckerBoard[spot.getX()][j]=1;
                    }
                }else {
                    //如果不为空,则直接从上一步中取值
                    finalCheckerBoard=stack.peek().getCheckerboard();
                    //因为之前没做过标记,这里回溯时前进一个防止重复
                    finalCheckerBoard[spot.getX()][spot.getY()]=1;
                }
                return findOne(finalCheckerBoard, step.getNumber());
            }
            if (stack.isEmpty()&&spot.getX()==0&&spot.getY()==7){
                flag=false;
            }
            //如果等于7,说明此皇后遍历完了,下一次得从上一个开始遍历
            return null;

    }



    /**
     * 在处理好的棋盘中找第number个皇后可以摆放的位置
     * @param handledcheckerboard 处理好的棋盘
     * @param number 第number个皇后
     * @return 摆放的位置
     */
    private static Spot findOne(int[][] handledcheckerboard,int number) {
        Spot spot=new Spot();
        int i=number-1;
        for (int j=0;j<8&&i<8;j++){
            if (handledcheckerboard[i][j]==0){
                spot.x=i;
                spot.y=j;
                return spot;
            }
        }

        return null;
    }


    /**
     * 处理棋盘
     * @param spot 皇后坐标
     * @return 处理好的棋盘
     */
    private static int[][] handle(Spot spot) {
        int x= spot.x;
        int y= spot.y;
        int[][] checkerboard=new int[8][8];
        for (int j=0;j<8;j++){
            checkerboard[x][j]=1;
        }
        for (int i = 0; i < 8; i++) {
            checkerboard[i][y]=1;
        }

        //处理对角线

        for (int i=0,j=y-x;i<8;i++,j++){
            if (j>=0&&j<8){
                checkerboard[i][j]=1;
            }
        }
        for (int i=0,j=x+y;i<8;i++,j--){
            if (j>=0&&j<8){
                checkerboard[i][j]=1;
            }
        }

        return checkerboard;
    }

    /**
     * 合并处理好的棋盘
     * @param checkerboards 处理好的棋盘
     * @return 合并好的棋盘
     */
    private static int[][] merge(int[][]...checkerboards) {
        int[][] checkerboard=new int[8][8];
        for (int[][] c:checkerboards){
            for (int i=0;i<8;i++){
                for (int j=0;j<8;j++){
                    if (c[i][j]==1){
                        checkerboard[i][j]=1;
                    }
                }
            }
        }
        return checkerboard;
    }

    /**
     * 记录点的坐标
     */
    private static class Spot{
        int x;
        int y;

        public Spot() {
        }

        public Spot(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }

        @Override
        public String toString() {
            return "Spot{" +
                    "x=" + x +
                    ", y=" + y +
                    '}';
        }
    }

    /**
     * 记录每一步,便于存储和回溯
     */
    private static class Step{
        Spot spot;
        int[][] checkerboard;
        //记录序号
        int number;

        public Step() {
        }

        public Step(Spot spot, int[][] checkerboard, int number) {
            this.spot = spot;
            this.checkerboard = checkerboard;
            this.number = number;
        }

        public Spot getSpot() {
            return spot;
        }

        public void setSpot(Spot spot) {
            this.spot = spot;
        }

        public int[][] getCheckerboard() {
            return checkerboard;
        }

        public void setCheckerboard(int[][] checkerboard) {
            this.checkerboard = checkerboard;
        }

        public int getNumber() {
            return number;
        }

        public void setNumber(int number) {
            this.number = number;
        }
    }
}

运行截图

在这里插入图片描述

记录点滴,乐于分享,转载请注明出处。

愿我们能以梦为马,不负人生韶华! 与君共勉!

  • 作者:金昊霖
  • 发表时间:2020-7-04
  • 版权声明:自由转载-非商用-非衍生-保留署名(创意共享3.0许可证)
  • 评论

    留言