boxmoe_header_banner_img

欢迎来到YaeMiko小站

加载中

文章导读

java八皇后解法


avatar
Samele 2025-05-10 29

分享一段没什么实用性的代码。

背景:

八皇后问题(Eight queens puzzle)是由国际象棋棋手马克斯·贝瑟尔(Max Bezzel)于1848年提出的经典数学与算法问题。其核心规则为:

棋盘限制‌:在8×8格的国际象棋棋盘上放置8个皇后。
‌攻击规则‌:皇后可以横向、纵向、斜向移动,因此任意两个皇后不能处于同一行、同一列或同一斜线上。

解的总数‌:共有92种不同的摆放方案

历史与研究进展
‌早期探索‌:高斯曾认为有76种方案,但1854年象棋杂志上发表的解增至40种,后通过图论方法验证为92种。
‌计算机求解‌:计算机发明后,该问题成为回溯算法的典型案例,被多种编程语言实现。

 

本来想用作练习递归和回溯算法使用。后来自己尝试了很久ai也问了,也参考了各路大神的方法。发现实在是难以理解(脑子不灵光),

于是乎尽力写了一份自己能理解的代码出来。即使代码非常冗余,仅用于记录思路,仅供参考:

 

思路:

比如是8个皇后,即棋盘大小是8×8的

第一步:初始化棋盘:将棋盘所有位置标为0

将所有行的第一列标为皇后,标为1

第二部:从最后一行开始,将皇后向后移动一个位置,开始判断所有皇后是否会互相攻击到。直到最后一行皇后挪动到了最后的一列。

第三步:将处理的行(注解中提到的处理光标7)向上移动一行到6,移动一行后按照逻辑不会进入最后的do-while循环,

而是会return到处理光标为7的栈,再次开始循环。直到该光标行的所有皇后已经处于最后一列了。

 

 

import java.util.Scanner;
class Queens {
    public static int count = 0;
    //程序入口,通过窗口输入皇后的个数,调用gameStart方法
    public static void main(String[] args){
         Scanner scanner = new Scanner(System.in);
         System.out.println("请输入皇后的个数,进行求解");
         int x = scanner.nextInt();
         gameStart(x);
         System.out.println(x + "个皇后,共有" + count + "种解");


    }
    //生成一个与皇后个数相同的二维数组棋盘,同时将棋盘填满参数0,将棋盘对象交给Queens方法处理,Queens(map,0),
    //第一个参数代表棋盘,第二个参数代表需要处理的行。
    public static void gameStart(int x) {
        int[][] map = new int[x][x];

        for(int i = 0; i < map.length; ++i) {
            for(int j = 0; j < map[i].length; ++j) {
                map[i][j] = 0;
            }
        }

        Queens(map, 0);
    }
    //开始棋盘处理逻辑
    public static void Queens(int[][] map, int y) {
        //当前正在处理的行数大于等于0,防止数组越界
        if (y >= 0) {
            //如果该行没有皇后!说明游戏才开始,将所有皇后放在第一列。同时将处理光标挪动至最后一行
            if (!currentLineHaveQueen(map, y)) {
                queenBegin(map);
                y = map.length - 1;
            }
            //如果,处理光标==棋盘的数量-1,即在棋盘最后一行了,且当前行皇后已经在最后一列了,即棋盘最后一行最后一列已经有皇后了
            if (y == map.length - 1 && currentQueenAtEnd(map, y)) {
                //将处理光标挪到上一行,找到上一行的皇后,将上一行的皇后去向后挪动一个位置。
                queenMoveNext(map, y - 1, findQueenAtX(map, y - 1));
                //当然,如果上一行(比如说倒数第二行的皇后已经在最后一个位置,则保持这个位置不变)
            }
            //如果,处理光标不是最后一行 且 光标所在行有一个皇后 且 处理光标下面的所有皇后都在最后一列了 且 这一行的皇后也在最后一列了
            if (y != map.length - 1 && currentLineHaveQueen(map, y) && afterQueenAllAtEnd(map, y) && currentQueenAtEnd(map, y)) {
                //重复调用这个方法,将光标向上移动一行
                Queens(map, y - 1);
            }
            //如果,处理光标不是最后一行 且 当前行有一个皇后 且 处理光标下面所有的皇后都在最后一列了 且 当前行的皇后不在最后一列
            if (y != map.length - 1 && currentLineHaveQueen(map, y) && afterQueenAllAtEnd(map, y) && !currentQueenAtEnd(map, y)) {
                //将处理光标下面几行的皇后位置恢复到最初位置
                resetAfterQueens(map, y);
                //将本行的皇后向后挪动一个位置
                queenMoveNext(map, y, findQueenAtX(map, y));
            //否则
            } else {
                //如果处理光标在最后一行 且 当前行有一个皇后,
                //这个判断的意义在于:如果该方法进行了递归调用。(用于处理上面几行的皇后位置)到这个语句中因为处理光标不在最后一行,
                //固会退出循环,防止死循环。当递归方法退出并回到这个调用方法时,因为没有到达break条件:(即所有皇后都在最后一列了)
                //那么该栈的y(处理光标行数,依然在最后一行)将从0开始进行遍历。
                //整个程序的逻辑就如同拨算盘一样的进行累加。
                //退出的条件有2,其一就是所有皇后都在最后一列了,第二是,处理光标,即y<0了。不会进入递归处理逻辑。
                if (y == map.length - 1 && currentLineHaveQueen(map, y)) {
                    //从0开始,将皇后一次放置在每一列进行判断,如果找到一个解,则打印出来
                    do {
                        for(int i = 0; i < map[y].length; ++i) {
                            if (aSolution(map)) {
                                ++count;
                                printMap(map, count);
                            }
    
                            queenMoveNext(map, y, findQueenAtX(map, y));
                        }
                        //重新调用这个方法,将处理光标移动至上一行
                        Queens(map, y - 1);
                    //退出条件:所有皇后都在最后一列了
                    } while(!queenBreak(map));
                }

            }
        }
    }

    //游戏开始方法。将所有行最初始的位置放置一个皇后
    public static void queenBegin(int[][] map) {
        for(int i = 0; i < map.length; ++i) {
            map[i][0] = 1;
        }

    }
    //退出条件。最有行的最后一列都有一个皇后了
    public static boolean queenBreak(int[][] map) {
        for(int i = 0; i < map.length; ++i) {
            int len = map[i].length - 1;
            if (map[i][len] == 0) {
                return false;
            }
        }

        return true;
    }
    //将处理光标y所在行的皇后清除,将这行的皇后放在第一个位置
    public static void queenReset(int[][] map, int y) {
        for(int i = 0; i < map[y].length; ++i) {
            map[y][i] = 0;
        }

        map[y][0] = 1;
    }
    //如果当前行有一个皇后,则放回true,否则返回false
    public static boolean currentLineHaveQueen(int[][] map, int y) {
        for(int i = 0; i < map[y].length; ++i) {
            if (map[y][i] == 1) {
                return true;
            }
        }

        return false;
    }
    //如果当前行的皇后在最后一个位置,则返回true,否则返回false
    public static boolean currentQueenAtEnd(int[][] map, int y) {
        return map[y][map[y].length - 1] == 1;
    }
    //如果当前所在行的后面所有行的皇后都在最后一列,返回true,否则返回false
    public static boolean afterQueenAllAtEnd(int[][] map, int y) {
        for(int i = y + 1; i < map.length; ++i) {
            if (map[i][map[i].length - 1] == 0) {
                return false;
            }
        }

        return true;
    }
    //找到当前行皇后所在位置
    public static int findQueenAtX(int[][] map, int y) {
        for(int i = 0; i < map[y].length; ++i) {
            if (map[y][i] == 1) {
                return i;
            }
        }

        return -1;
    }
    //将当前行的后面所有行的皇后位置重置
    public static void resetAfterQueens(int[][] map, int y) {
        for(int i = y + 1; i < map.length; ++i) {
            queenReset(map, i);
        }

    }
    //当前行皇后位置向后移动一位
    public static void queenMoveNext(int[][] map, int y, int x) {
        if (x != map[y].length - 1) {
            map[y][x] = 0;
            map[y][x + 1] = 1;
        }

    }
    //如果找到一个解,则返回true,否则返回false
    public static boolean aSolution(int[][] map) {
        for(int i = 0; i < map.length; ++i) {
            for(int j = 0; j < map[i].length; ++j) {
                if (map[i][j] == 1 && !isSafe(i, j, map)) {
                    return false;
                }
            }
        }

        return true;
    }
    //判断当前棋盘的所有皇后是否安全
    public static boolean isSafe(int y, int x, int[][] map) {
        int i;
        for(i = 0; i < map[y].length; ++i) {
            if (i != x && map[y][i] == 1) {
                return false;
            }
        }

        for(i = 0; i < map.length; ++i) {
            if (i != y && map[i][x] == 1) {
                return false;
            }
        }

        i = y;
        int j = x;

        while(i > 0 && j > 0) {
            --i;
            --j;
            if (map[i][j] == 1) {
                return false;
            }
        }

        i = y;
        j = x;

        while(i < map.length - 1 && j > 0) {
            ++i;
            --j;
            if (map[i][j] == 1) {
                return false;
            }
        }

        i = y;
        j = x;

        while(i < map.length - 1 && j < map[i].length - 1) {
            ++i;
            ++j;
            if (map[i][j] == 1) {
                return false;
            }
        }

        i = y;
        j = x;

        while(i > 0 && j < map[i].length - 1) {
            --i;
            ++j;
            if (map[i][j] == 1) {
                return false;
            }
        }

        return true;
    }
    //打印棋盘方法
    public static void printMap(int[][] map, int count) {
        System.out.println("==========第" + count + "种解=========");

        for(int i = 0; i < map.length; ++i) {
            for(int j = 0; j < map[i].length; ++j) {
                System.out.print(map[i][j] + "  ");
            }

            System.out.println();
        }

    }
}

 



评论(已关闭)

评论已关闭