八皇后问题

八皇后问题的背景:1848年,国际西洋棋手贝瑟尔提出在 8x8 的国际象棋上摆放八个皇后,任意两个皇后不能处于同一行、同一列或者同一斜线上,问一共有多少种摆法?

  思路分析:

  1. 第1个皇后先放在第1行第1列,
  2. 第2个皇后放在第2行第1列,然后判断是否满足条件,不满足就继续放在第2行第3列、第2行第4列,直到在第2行的8列中找到合适的列位置,
  3. 第3~8个皇后与第2个皇后做法一样,
  4. 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,第1个皇后放到第1行第1列的所有解全部得到
  5. 继续将第1个皇后放第1行第2列,循环执行2、3、4步骤;

简而言之,第一个皇后位置固定,找到剩下所有满足条件的皇后排列的可能情况。 

我们要着重理解这句话:当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,第1个皇后放到第1行第1列的所有解全部得到

以下图为例:

  • 当得到1个正确解时:第1个皇后放在第1行的第1列,第1行共有8列,共有8种放法,每一种对应方法对应其他皇后的结果不同,
  • 在栈回退到上一个栈时,就会开始回溯:回溯就是判断我们之前放置的皇后是否正确,放置完第3个皇后,遍历判断和前两个皇后的关系正确与否,

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。

代码实现:

棋盘的表示方法:使用一维数组来表示,array[i]的i:放第i+1个皇后,array[i] = thcol:第i+1个皇后放在第thcol列;

int max = 8; // 八个皇后
int[] array = new int[max];
static int totalCount = 0;

第2个皇后放在第2行第1列,然后判断是否满足不在同一行、同一列、同一斜线,不满足就继续放在第2行第3列、第2行第4列,直到在第2行的8列中找到合适的列位置 ;

// 当放第n个皇后时,判断该皇后和前面已经放置的皇后是否在同一列或者斜线
// true表示不在同一列,不在同一斜线
public boolean judge(int queue) {
    for (int row = 0; row < queue; row++) {
        if (array[row] == array[queue] || Math.abs(queue - row) == Math.abs(array[queue] - array[row])) {
            return false;
        }
    }
    return true;
}

使用递归,找到所有的解法,第queue皇后满足条件,就查找第queue+1个皇后满足条件的情况,直到8个皇后不同位置的不同方法都找到

public void check(int queue) {
    if (queue == max) { // queue一共是8个
        show();
        totalCount++;
        return;
    }
    for (int col = 0; col < max; col++) { // 从第0列到第7列
        array[queue] = col; // 先把皇后放在第1列,queue不仅代表第几个皇后,也代表第几行
        if (judge(queue)) { //皇后不在同一列,同一斜线
            check(queue + 1); // 接着放queue+1个皇后
        }
    }
}

 全部代码:

package cn.cwj.recursion;

public class Queue8 {
int max = 8; // 最多放置皇后的数量
// 一维数组表示棋盘,array[i]的i:放第i+1个皇后,array[i] = thcol:第i+1个皇后放在第thcol列
int[] array = new int[max];
static int totalCount = 0;

    public void check(int queue) {
        if (queue == max) { // queue一共是8个
            show();
            totalCount++;
            return;
        }
        for (int col = 0; col < max; col++) { // 从第0列到第7列
            array[queue] = col; // 先把皇后放在第1列,queue不仅代表第几个皇后,也代表第几行
            if (judge(queue)) { //皇后不在同一列,同一斜线
                check(queue + 1); // 接着放queue+1个皇后
            }
        }
    }

    // 当放第n个皇后时,判断该皇后和前面已经放置的皇后是否在同一列或者斜线
    // true表示不在同一列,不在同一斜线
    public boolean judge(int queue) {
        for (int row = 0; row < queue; row++) {
            /*
                皇后在同一列,同一斜线的判断条件:
                1).array[row] == array[thQueue] 表示第row行的列值和第queue行的第queue个皇后是否在同一列上
                    1.1)row从0开始,表示 在棋盘上 放置当前的 第queue个皇后 会和之前的 已经放置的皇后 依次比较,同列返回false
                2).Math.abs(n-row) == Math.abs(array[n]-array[i])),表示皇后在同一斜线
                    2.1)n-i是行相减,
                    2.2)array[n]-array[i]是列相减,把棋盘看成正方形,斜线是对角线,构成等边直角三角形,用坐标计算思想即可
                    2.3)比如,当前行(row,array[row]),当前皇后(queue,array[queue])转成坐标系
            */
            if (array[row] == array[queue] || Math.abs(queue - row) == Math.abs(array[queue] - array[row])) {
                return false;
            }
        }
        return true;
    }

    // 显示放置皇后的位置
    public void show() {
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + "\t");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Queue8 queue8 = new Queue8();
        queue8.check(0);// 从第1个皇后开始放
        System.out.println("八皇后问题共有"+totalCount+"解法");
    }
}

 截取了部分结果图:可以根据打印的结果到小游戏中搜索“死亡八皇后”进行验证

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值