生活日用算法——八皇后问题

八皇后问题也算是比较经典的回溯算法的经典案例。题干描述如下:

在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法

对此首先我们使用array[][]来构建一个棋盘,然后尝试落子,此时算法如下:

   /**
    * 寻找皇后节点
    * @param row
    * @param size
    */
    public static void findQueen(int row, int size) {
        // 如果皇后行已达到最后一行,结果数量自增,并未输出当前棋盘情况
        if (row == size) {
            resultCount++;
            print(size);
            return;
        }

        // 递归回溯
        for (int column = 0; column < size; column++) {
            // 检查当前节点是否可以放皇后
            if (check(row, column, size)) {
                // 使用皇后占位
                array[row][column] = 1;
                // 查找下一列的可占位节点
                findQueen(row + 1, size);
                // 继续尝试同一行其他列占位前,清空临时占位
                array[row][column] = 0;
            }
        }
    }

其中check方法实现如下:

 /**
     * 判断节点是否合适
     *
     * @param row    行
     * @param column 列
     * @param size   棋盘尺寸
     * @return
     */
    public static boolean check(int row, int column, int size) {

        // 遍历
        for (int rowTemp = row - 1; rowTemp >= 0; rowTemp--) {
            // 验证纵向
            if (array[rowTemp][column] == 1) {
                return false;
            }

            int offset = row - rowTemp;
            int columnLeft = column - offset, columnRight = column + offset;
            // 验证左向
            if (columnLeft >= 0 && array[rowTemp][columnLeft] == 1) {
                return false;
            }

            // 验证右向
            if (columnRight < size && array[rowTemp][columnRight] == 1) {
                return false;
            }
        }

        return true;
    }

完整算法如下:

public class EightTest {
    public static int[] array;//棋盘,放皇后
    public static int resultCount = 0;//存储方案结果数量

    public static void main(String[] args) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        int size = 14;
        array = new int[size];
        findQueen(0, size);
        System.out.println(size + "皇后问题共有:" + resultCount + "种可能,耗时:"+stopwatch.stop().toString());
    }

    /**
     * 寻找皇后节点
     *
     * @param row
     * @param size
     */
    public static void findQueen(int row, int size) {
        // 如果皇后行已达到最后一行,结果数量自增,并未输出当前棋盘情况
        if (row == size) {
            resultCount++;
            print(size);
            return;
        }

        // 递归回溯
        for (int column = 0; column < size; column++) {
            // 检查当前节点是否可以放皇后
            if (check(row, column, size)) {
                // 使用皇后占位
                array[row] = column;
                // 查找下一列的可占位节点
                findQueen(row + 1, size);
                // 继续尝试同一行其他列占位前,清空临时占位
                array[row] = 0;
            }
        }
    }

    /**
     * 判断节点是否合适
     *
     * @param row    行
     * @param column 列
     * @param size   棋盘尺寸
     * @return
     */
    public static boolean check(int row, int column, int size) {

        // 遍历
        for (int rowTemp = row - 1; rowTemp >= 0; rowTemp--) {
            // 验证纵向
            if (array[rowTemp] == column) {
                return false;
            }

            int offset = row - rowTemp;
            int columnLeft = column - offset, columnRight = column + offset;
            // 验证左向
            if (columnLeft >= 0 && array[rowTemp] == columnLeft) {
                return false;
            }

            // 验证右向
            if (columnRight < size && array[rowTemp] == columnRight) {
                return false;
            }
        }

        return true;
    }

    public static void print(int size) {//打印结果
        System.out.println("方案" + resultCount + ":");
        for (int i = 0; i < size; i++) {
            for (int m = 0; m < size; m++) {
                System.out.print((array[i] == m ? 1 : 0) + " ");
            }
            System.out.println();
        }
        System.out.println();
    }
}

继续优化时间复杂度,我们仔细观察输出的棋盘信息,举个例子:

方案92:
0 0 0 0 0 0 0 1
0 0 0 1 0 0 0 0
1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0
0 0 0 0 0 1 0 0
0 1 0 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0 

来考虑是否能把每一列当做一个二进制数来处理,这样我们判断第N列时,其实对于向上判断,只需要进行位运算即可。具体代码如下:

public class EightTest {

    public static Integer size = 15;
    // 棋盘,放皇后
    public static int[] array = new int[size];

    // 映射
    public static int[] arrayMapping = new int[size];

    // 映射
    public static int[][] arrayMappingDeep = new int[size][size];

    // 记录每一列已经被占用的值——2进制
    public static int a = 0;

    // 存储方案结果数量
    public static int resultCount = 0;

    public static void main(String[] args) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        arrayMapping[0] = 0b1;

        for (int i = 1; i < size; i++) {
            arrayMapping[i] = arrayMapping[i - 1] << 1;
        }

        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                arrayMappingDeep[i][j] = arrayMapping[i] | (i + j >= size ? 0 : arrayMapping[i] << j) | arrayMapping[i] >> j;
            }
        }

        findQueen(0, size);
        System.out.println(size + "皇后问题共有:" + resultCount + "种可能,耗时:" + stopwatch.stop().toString());
    }

    /**
     * 寻找皇后节点
     *
     * @param row
     * @param size
     */
    public static void findQueen(int row, int size) {
        // 如果皇后行已达到最后一行,结果数量自增,并未输出当前棋盘情况
        if (row == size) {
            resultCount++;
            print(size);
            return;
        }

        // 递归回溯
        for (int column = 0; column < size; column++) {

            a = 0;
            for (int i = 0; i < row; i++) {
                a = a | arrayMappingDeep[array[i]][row - i];
            }

            // 检查当前节点是否可以放皇后
            if (check(column)) {
                // 使用皇后占位
                array[row] = column;
                // 查找下一列的可占位节点
                findQueen(row + 1, size);
                // 继续尝试同一行其他列占位前,清空临时占位
                array[row] = 0;
            }
        }
    }

    /**
     * 判断节点是否合适
     *
     * @param column 列
     * @return
     */
    public static boolean check(int column) {

        return (a & arrayMapping[column]) == 0;
    }

    public static void print(int size) {//打印结果
        System.out.println("方案" + resultCount + ":");
        for (int i = 0; i < size; i++) {
            for (int m = 0; m < size; m++) {
                System.out.print((array[i] == m ? 1 : 0) + " ");
            }
            System.out.println();
        }
        System.out.println();
    }
}

本地执行没跑出明细差别,leetcode上前者写法7ms,后者4ms。感觉如果要刷leetcode,需要修改输出格式增加缓存来拼一下时间。

原文地址:https://www.cnblogs.com/fbw-gxy/p/12584308.html

时间: 2024-10-12 07:54:07

生活日用算法——八皇后问题的相关文章

生活日用算法之NIM博弈

故事还是得从笔者某次去ktv说起.席间见到某汉子拿出15个色子,分成3,5,7三组,开始和妹子按照以下规则玩耍: 1.每次可以从任意一组中取任意个色子 2.取到最后一组的人赢 任意先后手,大多是某汉子一直赢,然后妹子一直喝,然后... 故事背景大概就是这样,回去后我尝试用枚举的方式去寻找必胜策略.大概可以等到以下形式的必胜组合2,2;3,3;4,4;5,5;1,2,3;1,4,5等,但转念一想如果玩的多了妹子也背下了必胜组合,我方就必须得实时更新自己脑海中的必胜组合,这不符合我们一个理科生的世界

回溯算法解八皇后问题(java版)

八皇后问题是学习回溯算法时不得不提的一个问题,用回溯算法解决该问题逻辑比较简单. 下面用java版的回溯算法来解决八皇后问题. 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 思路是按行来规定皇后,第一行放第一个皇后,第二行放第二个,然后通过遍历所有列,来判断下一个皇后能否放在该列.直到所有皇后都放完,或者放哪

【八皇后问题】 回溯算法

回溯算法:回溯算法实际上是一个类似枚举的搜索尝试方法,它的思想是在搜索尝试中寻找问题的解,当发现不满足求解条件时,就“回溯”返回,尝试别的路径.之前介绍的基础算法中的贪婪算法,动态规划等都具有“无后效性”,也就是在分段处理问题时,某状态一旦确定,将不再改变.而多数问题很难找到"无后效性”的阶段划分和相应决策,而是通过深入搜索尝试和回溯操作完成的. 八皇后问题:8*8的国际象棋棋盘中放八个皇后,是任意两个皇后不能互相吃掉.规则:皇后能吃掉同一行,同一列,同一对角线的任意棋子. 模型建立:不妨设八个

求解八皇后问题的退火算法

这个算法收敛速度还算满意.此算法可以计算n皇后问题,只需要将n改为相应整数即可! 主程序: clear; clc; %% %八皇后问题,8X8的棋盘上,放置8个皇后,使之两两都不能攻击 %使用退火算法计算 %初始的状态,随机在棋盘上放置8个皇后,每列放一个,使每一行都不能攻击 n = 8; %8皇后 %% %产生一个随机的状态 state = randperm(n); %% %计算当前状态的h函数值(与目标状态的差距,h越大,差距越大.h=0代表此状态即为目标状态) h = fun_c(stat

Python学习二(生成器和八皇后算法)

看书看到迭代器和生成器了,一般的使用是没什么问题的,不过很多时候并不能用的很习惯 书中例举了经典的八皇后问题,作为一个程序员怎么能够放过做题的机会呢,于是乎先自己来一遍,于是有了下面这个ugly的代码 def table(m, lst): '''绘制m列的棋盘,每行有个皇后旗子''' head = '┌' + '─┬' * (m-1) + '─┐' row = lambda x: '│' + ' │' * x + '╳│' + ' │' * (m - x - 1) trow = '├' + '─

【算法】用Lua解决八皇后的问题

最近在学习Lua脚本,经过了不到十天的学习,也算是对语法有所了解吧,另外正好也看到了八皇后问题,感觉挺有意思的 就试了试用算法解出来. 八皇后问题的原题是:八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行.纵行或斜线上. 以下是lua的算法代码: local eightQueen = { 0,0,0,0,0,0,0,0,} local count = 0 functi

递归之回朔算法应用----八皇后问题

从前,有个皇帝,取了八个皇后,由此产生一系列乱七八糟的问题,八皇后问题由此产生.哈哈 开个玩笑~~~~ 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 高斯认为有76种方案.1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果.计算机发明后,有多种方法可以解决此问题.

算法题(八皇后)

问题: 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法? 代码: public class huanghou{ private int[] column;//同栏是否有皇后,1表示有 private int[] rup;//右上至左下是否有皇后 private int[] lup;//左上至右下是否有皇后 pri

八皇后算法的另一种实现(c#版本)

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