八皇后问题 DFS Java版

看了不同的博客,有用一维数组来解决的,也有用二维数组来解决的,思路基本上都是一致的。

八皇后问题—Java—递归回溯_南疆晚歌的博客-CSDN博客https://blog.csdn.net/qq_45629699/article/details/109146182【图解回溯算法】如何用回溯算法来解决8皇后问题?_hixiaoxiaoniao的专栏-CSDN博客_八皇后问题https://blog.csdn.net/hixiaoxiaoniao/article/details/109509977放上一个链接,可以检验你代码结果对不对,多测试几个即可(我测试了几个都没问题)

N皇后问题解法及解的个数_张小帅的专栏-CSDN博客_n皇后问题有多少解https://blog.csdn.net/qinghezhen/article/details/17849837第二个链接【图解回溯算法】里有动图,很直观地反映了八皇后问题的解决过程。一维数组,index表示当前行,arr[index]表示第index行的皇后存放在arr[index]列。在if语句check的时候,不存在修改原来arr的值,因为直接下一次for循环会直接覆盖。但是对于二位数组来说,没有记录具体的列,只是这一项被赋值为1,所以在DFS回溯的时候要把当前“地图上”这一项换成0。

在check函数的编写上,一维数组和二维数组也不同。

不管是哪一个check函数,都是为了检查在当前行这个位置摆放皇后是否合适,下面的行都还没有进行摆放,所以不用管,只需检查上面的行的摆放情况就好。

check函数需要检查两个:

①同一列上,是否已经摆放过皇后

②斜对角线上是否已经摆放过皇后,斜对角线包括两个方向,一个是平行于y=x的斜对角线,一个是平行于y=-x的斜对角线

现在按照上面的两种情况,现在来解释一下两个check函数

①一维数组的check函数,一维数组,index记录行数,arr[index]记录列数

第一个括号内的就是检查是否在同一列上,第二个括号内就是检查两个斜对角线的情况,因为使用了abs函数,所以正斜对角线和负斜对角线都可以囊括在内

(arr[i] == arr[n]) || (Math.abs(n - i) == Math.abs(arr[n] - arr[i])

②二维数组的check函数,二维数组表示布局,即元素为1的位置表示皇后的摆放位置

check函数中的第一个for循环,是检查同一列上是否已经摆放过皇后,第二个while循环是检查与y=x平行的那条对角线是否摆放过皇后,第三个while循环是检查y=-x平行的那条对角线是否摆放过皇后,后面两个while循环在编写的时候要注意循环条件!

代码中的maxn代表n皇后问题的n。

一维数组代码

public class Main {

    int[] arr;
    int cnt;
    int maxn;
//    int flag = 0;

    public static void main(String[] args) {
        new Main().solve();
    }

    private void solve() {
        //用一维数组记录八皇后的摆放问题,数组的index表示第几行,数组的value表示当前行的皇后摆在第几列
        maxn = 9;
        arr = new int[maxn];
        cnt = 0;
        dfs(0);
        System.out.println(cnt);
    }

    private void dfs(int n) {

        if (n == maxn) {
            //表示8个皇后已经全部放置完毕
            cnt++;
//            if (flag == 0) {
//                for (int i = 0; i < maxn; i++) {
//                    for (int j = 0; j < maxn; j++) {
//                        //arr[i]表示第i行摆在哪一列
//                        if (arr[i] != j) {
//                            System.out.print("0");
//                        } else {
//                            System.out.print("1");
//                        }
//                    }
//                    System.out.println();
//                }
//                flag = 1;
//            }
            return;
        }
        //每一列去摆放皇后
        for (int i = 0; i < maxn; i++) {
            //先放置,再判断
            //就我个人理解,这里必须把赋值操作放在外面,是因为这里使用一维数组来记录,没有过多的信息来判断
            //判断的是,在第n行的这个位置是否合适
            //如果想要把赋值语句拿到if里面,那么必须向check函数再传递一个参数,即你假设把当前这个皇后放在哪
            arr[n] = i;
            if (check(n)) {
                dfs(n + 1);
            }

//            arr[n] = i;
//            if (check(n, i)) {
//                dfs(n + 1);
//            }
        }
    }

    private boolean check(int n) {
        for (int i = 0; i < n; i++) {
            if ((arr[i] == arr[n]) || (Math.abs(n - i) == Math.abs(arr[n] - arr[i]))) {
                return false;
            }
        }
        return true;
    }

    private boolean check(int n, int index) {
        for (int i = 0; i < n; i++) {
            if ((arr[i] == index) || (Math.abs(n - i) == Math.abs(index - arr[i]))) {
                return false;
            }
        }
        return true;
    }
}

二位数组代码

public class Main {

    int maxn = 2;
    int[][] arr = new int[maxn][maxn];
    int cnt;

    public static void main(String[] args) {
        new Main().solve();
    }

    private void solve() {
        cnt = 0;
        dfs(0);
        System.out.println(cnt);
    }

    private void dfs(int n) {
        if (n == maxn) {
            cnt++;
            return;
        }
        for (int i = 0; i < maxn; i++) {
//            arr[n][i] = 1;
//            if (check(n, i)) {
//                dfs(n + 1);
//            }
//            arr[n][i] = 0;
            if (check(n, i)) {
                arr[n][i] = 1;
                dfs(n + 1);
                arr[n][i] = 0;
            }
            //这两种方式思路其实是一样的。
            //就下面这种来说,首先判断这个点的位置是否满足,如果满足的话,把这个位置设置为1,继续去下一行找合适的位置
            //下一行如果没有位置是满足的,回退到这一行,这个位置重新设置成0,再往后找合适的位置
        }
    }

    private boolean check(int n, int m) {
        //当前这个点在第n行,第m列
        //第0行到该行
        for (int i = 0; i < n; i++) {
            //同一列上有
            if (arr[i][m] == 1) {
                return false;
            }
        }
        int n1 = n - 1;
        int m1 = m - 1;
        while (n1 >= 0 && m1 >= 0) {
            if (arr[n1][m1] == 1) {
                return false;
            }
            n1--;
            m1--;
        }
        n1 = n - 1;
        m1 = m + 1;
        while (n1 >= 0 && m1 < maxn) {
            if (arr[n1][m1] == 1) {
                return false;
            }
            n1--;
            m1++;
        }
        return true;
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java八皇后问题是一个经典的回溯算法问题,它的目标是在一个8x8的棋盘上放置8个皇后,使得它们不互相攻击,即任意两个皇后都不能在同一行、同一列或者同一对角线上。动态规划算法可以通过状态转移方程来实现,具体实现过程可以参考以下步骤: 1. 定义状态:使用一个一维数组 queens 来表示棋盘的放置状态,queens[i] 表示第 i 行皇后所在的列数。 2. 定义状态转移方程:对于第 i 行皇后,它可以放置在 0 ~ 7 中的任意一个位置,因此可以得到状态转移方程 queens[i] = j,其中 j 表示第 i 行皇后所在的列数,同时需要保证该位置不与之前已经放置的皇后冲突,即 queens[i] != queens[k] && abs(queens[i] - queens[k]) != abs(i - k),其中 k 表示已经放置的皇后行数。 3. 初始化:初始化 queens 数组为 -1,表示所有皇后都未放置。 4. 递归回溯:从第一行开始递归回溯,在每一行中遍历所有可能的列数,判断当前位置是否合法,如果合法则继续递归下一行,否则回溯到上一行重新选择位置。 以下是Java八皇后问题的动态规划算法实现代码: ``` public class EightQueens { private static final int N = 8; // 皇后数量 private static int[] queens = new int[N]; // 皇后所在列数 public static void main(String[] args) { Arrays.fill(queens, -1); // 初始化为未放置状态 dfs(0); // 从第一行开始递归回溯 } private static void dfs(int row) { if (row == N) { // 所有皇后都已放置完毕,输出结果 System.out.println(Arrays.toString(queens)); return; } for (int col = 0; col < N; col++) { // 枚举当前行所有可能的列数 if (isValid(row, col)) { // 判断当前位置是否合法 queens[row] = col; // 放置皇后 dfs(row + 1); // 递归下一行 queens[row] = -1; // 回溯到上一行重新选择位置 } } } private static boolean isValid(int row, int col) { for (int i = 0; i < row; i++) { if (queens[i] == col || Math.abs(queens[i] - col) == Math.abs(i - row)) { return false; // 判断是否与之前放置的皇后冲突 } } return true; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值