n皇后问题(上:回溯解决)

496 阅读2分钟

这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

n皇后问题

八皇后问题英文Eight queens),是由国际象棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。

问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。如果经过±90度、±180度旋转,和对角线对称变换的摆法看成一类,共有42类。计算机发明后,有多种计算机语言可以编程解决此问题。

题目将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

image-20211116200643346

通过回溯解决

我们可以把这个问题看做一个树的模型,来进行查找,下面以3皇后来进行模拟一下这个模型

image-20211116202438806

其解决思想类似于深度优先遍历

先把皇后1放在第1行,然后向下寻找皇后2的位置,如果存在满足条件的位置就继续向下寻找如果所有位置都不满足条件则回溯到上一个皇后使她移到下一个位置

代码

我使用了数组来进行计算,但是思路都是一样的

(1)定义一个数组来存取棋子的位置(行)

(2)使用两个循环嵌套来进行查找

​ while循环来控制查找皇后编号

​ for循环来控制查找皇后位置

package demo1;

import java.util.Scanner;

/**
 * 回溯法解决皇后问题
 */
public class Tree {

    // 定义一个数组来存取棋子的位置(行)
    private static int[] num = new int[1000001];

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int i = 1;
        // 初始化0位置上位置为-1
        num[0] = -1;
        Queen(num, i, n);
    }

    private static void Queen(int[] num, int i, int n) {
        int j = 1; // 行数
        boolean flag = true; // 标置位
        while( i <= n){ // 开始寻找
            for (j = j ; j <= n; j++) {
                int k = 0;
                for ( k = 0; k < i; k++) {
                    if (j == num[k] || num[k] - k == j - i){
                        break;
                    }
                }
                if ( k >= i){
                    flag = false; // 说明皇后i可以放在i行中
                    break;
                }
            }
            if (flag){ // 回溯
                i--;
                j = num[i] + 1;
            }else { // 向下寻找
                num[i] = j;
                j = 1;
                i++;
            }
            flag = true; // 恢复标志位
        }
        for (int a = 1; a <=n ; a++) {
            System.out.print(num[a]+"\t");
        }
    }
}