八皇后问题

八皇后问题

八皇后问题(英文:Eight queens),是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。
问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。
在这里插入图片描述

回溯算法

定义

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。

八皇后问题就是回溯算法的典型,第一步按照顺序放一个皇后,然后第二步符合要求放第2个皇后,如果没有位置符合要求,那么就要改变第一个皇后的位置,重新放第2个皇后的位置,直到找到符合条件的位置就可以了。

回溯在迷宫搜索中使用很常见,就是这条路走不通,然后返回前一个路口,继续下一条路。回溯算法说白了就是穷举法。

八皇后问题如果用穷举法需要尝试88=16,777,216种情况。每一列放一个皇后,可以放在第 1 行,第 2 行,……,直到第8行。

回溯算法优于穷举法,其求解八皇后问题的原则是:有冲突解决冲突,没有冲突往前走,无路可走往回退,走到最后是答案。

八皇后问题解答

  1. 第一个皇后先放第一行第一列
  2. 第二个皇后放在第二行第一列、然后判断是否 OK, 如果不 OK,继续放在第二列、第三列、依次把所有列都放完,找到一个合适
  3. 继续第三个皇后,还是第一列、第二列……直到第 8 个皇后也能放在一个不冲突的位置,算是找到了一个正确解
  4. 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正确解, 全部得到.
  5. 然后回头继续第一个皇后放第二列,后面继续循环执行 1,2,3,4 的步骤

说明:
理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法,用一个一维数组即可解决问题. arr[8] =
{0 , 4, 7, 5, 2, 6, 1, 3} 对应 arr 下标 表示第几行,即第几个皇后,arr[i] = val , val 表示第 i+1 个皇后,放在第 i+1行的第 val+1 列。

代码实现

/**
 * 八皇后问题
 */
public class Queen8 {
    //定义一个max,表示一共有几个皇后
     static int max = 8;
     static int count = 0;
    //定义数组array,保存皇后放置位置的结果,比如arr= {0,4,7,5,2,6,1,3}
    static int[] array = new int[max];

    public static void main(String[] args) {
        //放置第0个
        check(0);
        System.out.println("一共有"+count+"种解法");
    }

    //放置第n个皇后
    private static void check(int n){
        if(n == max){ //8个皇后已经放好
            print();
            return;
        }
        //一次放入皇后并判断是否冲突
        for (int i = 0; i < max; i++) {
            //先把当前的皇后(第n个),放到该行的第一列
            array[n] = i;
            //判断当放置第n个皇后到i列是时候冲突
           if(judge(n)){
              //接着放第n+1个皇后
              check(n+1);
            }
           //如果冲突就继续执行array[n]=i;即将第n个皇后,放在本行的下一个位置
        }
    }

    /**
     *
     * @param n 表示第n个皇后
     * @return
     */
    private static boolean judge(int n){
        for (int i = 0; i < n; i++) {
            //array[i]==array[n] 判断时候在同一列
            //Math.abs(n-i)==Math.abs(array[n] - array[i]) 判断是否是同一斜线
            // n = 1放置第2列1 n = 1 array[1] = 1
            //Math.abs(1-0) == 1 Math.abs(array[n] - array[i]) = Math.abs(1-0) = 1
            //判断是否在同一行,没有必要,n每次都在递增
            if(array[i]==array[n] || Math.abs(n-i) == Math.abs(array[n] - array[i])) {
                return false;
            }
        }
        return true;
    }
    //写一个方法,输出皇后摆放的位置
    private static void print(){
        count++;
        for (int i = 0; i < array.length; i++) {
            System.out.print((array[i]+1)+" ");
        }
        System.out.println();
    }
}
代码分析:

可能看完代码大家会有些迷糊,我就在这里分析一下,比较难的代码地方,
首先要明确数组的索引值+1(下标)是第几行,数组中存放的值+1为第几列。就相当于坐标轴x,y的关系。
在对皇后位置判断的地方:

array[i]==array[n]
这个就是判断数组值是不是一样,一样的话就是在一列中,即判断两个坐标(x,y)中y值是否相等

Math.abs(n-i) == Math.abs(array[n] - array[i])

上述代码是为了判断相邻的两个皇后是否在同一个斜线上,即可以理解为有两个坐标(x1,y1)(x2,y2)
(x1-x2)的绝对值等于(y1-y2)的绝对值,如图
在这里插入图片描述
运行结果:
在这里插入图片描述
一共有92中解法。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值