当前位置: 首页 > article >正文

回溯算法:N皇后问题

N皇后问题是一个经典的回溯算法应用问题,要求在一个N×N的棋盘上放置N个皇后,使得它们互不攻击。即任何两个皇后都不能位于同一行、同一列或同一对角线上。这个问题可以通过回溯算法来解决,下面详细讲解这个问题的解法。

解题思路

  1. 逐行放置:一种有效的解决方案是逐行放置皇后,这样可以保证每行只有一个皇后。
  2. 检查冲突:放置每个皇后时,需要检查当前放置的皇后是否与已放置的皇后冲突(即检查列和对角线)。
  3. 回溯:如果当前行没有合法的列可以放置皇后,或者放置后无法在后续行找到合法放置位置,则需要回溯到上一行,改变皇后的位置继续尝试。
  4. 找到解后返回:当所有N个皇后都放置完毕时,记录下当前的棋盘布局作为一个解,然后回溯尝试其他可能的布局。

关键步骤

  1. 递归函数定义:定义一个递归函数,参数至少包含当前处理的行号和棋盘状态。
  2. 终止条件:当行号等于N时,说明已经成功放置了N个皇后,记录下当前解。
  3. 遍历当前行的每一列:尝试在当前行的每一列放置皇后。
  4. 检查是否可以放置皇后:对于当前选定的列,检查放置皇后后是否会与之前放置的皇后发生冲突。
  5. 递归尝试放置下一行的皇后:如果当前行的皇后放置成功,递归调用函数尝试放置下一行的皇后。
  6. 回溯:如果放置当前行的皇后后无法找到合法的解决方案,则需要撤销当前行的皇后放置,尝试当前行的下一列。

代码实现

代码实现中已经包含了详细的注释,解释了每一步的作用。关键点在于如何检查皇后之间是否互相攻击,这可以通过检查同一列和两个对角线来实现。对于对角线的检查,有两种对角线:一种是行号与列号之差相等的对角线,另一种是行号与列号之和相等的对角线。如果两个皇后位于同一对角线上,它们的行号与列号的差或和将相等。

通过回溯算法的深度优先搜索,我们可以找到所有可能的解决方案。每次尝试放置皇后时,如果发现当前选择导致没有合法的解决方案,就回溯到上一步,尝试其他可能的放置方式。这个过程重复进行,直到找到所有合法的解决方案或遍历完所有可能的放置方式。

import java.util.ArrayList;
import java.util.List;

public class NQueens {
    // 解集,存放所有可能的解
    private List<List<String>> solutions = new ArrayList<>();
    
    // 入口方法,n表示棋盘的大小
    public List<List<String>> solveNQueens(int n) {
        // 初始化棋盘,用'.'表示空,'Q'表示皇后
        char[][] board = new char[n][n];
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) {
                board[i][j] = '.';
            }
        }
        // 从第0行开始尝试放置皇后
        backtrack(board, 0);
        return solutions;
    }
    
    // 回溯方法,row表示当前操作的行
    private void backtrack(char[][] board, int row) {
        // 如果当前行超过了棋盘的行数,说明找到了一种解法
        if (row == board.length) {
            solutions.add(construct(board)); // 将当前解法添加到解集中
            return;
        }
        // 尝试在当前行的每一列中放置皇后
        for (int col = 0; col < board.length; col++) {
            // 检查在[row, col]位置放置皇后是否合法
            if (isValid(board, row, col)) {
                board[row][col] = 'Q'; // 放置皇后
                backtrack(board, row + 1); // 移动到下一行
                board[row][col] = '.'; // 回溯,撤销当前行的皇后
            }
        }
    }
    
    // 检查放置皇后是否合法
    private boolean isValid(char[][] board, int row, int col) {
        // 检查当前列是否有皇后
        for (int i = 0; i < row; i++) {
            if (board[i][col] == 'Q') {
                return false;
            }
        }
        // 检查左上对角线是否有皇后
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (board[i][j] == 'Q') {
                return false;
            }
        }
        // 检查右上对角线是否有皇后
        for (int i = row - 1, j = col + 1; i >= 0 && j < board.length; i--, j++) {
            if (board[i][j] == 'Q') {
                return false;
            }
        }
        return true;
    }
    
    // 构建解法
    private List<String> construct(char[][] board) {
        List<String> solution = new ArrayList<>();
        for (int i = 0; i < board.length; i++) {
            String row = new String(board[i]);
            solution.add(row);
        }
        return solution;
    }
    
    public static void main(String[] args) {
        NQueens solution = new NQueens();
        List<List<String>> solutions = solution.solveNQueens(4); // 解决4皇后问题
        for (List<String> sol : solutions) {
            for (String row : sol) {
                System.out.println(row);
            }
            System.out.println();
        }
    }
}

在这段代码中,solveNQueens方法初始化一个N×N的棋盘,并从第0行开始递归尝试放置皇后。backtrack方法负责在每一行尝试放置皇后,并检查是否合法。如果在当前行找到合法位置放置皇后,就递归调用自己尝试在下一行放置皇后。如果某一行无法放置皇后,则回溯到上一行,改变皇后的位置,再次尝试。

isValid方法用于检查在棋盘的[row, col]位置放置皇后是否会导致攻击现象,即检查当前列和两个对角线是否已经有皇后存在。

回溯算法的关键在于尝试并在遇到无法继续的情况下撤销(回溯)之前的尝试,寻找新的尝试路径。这样,通过枚举所有可能的情况,最终找到所有的解决方案。


http://www.kler.cn/a/232881.html

相关文章:

  • 批量重命名Excel文件并排序
  • 蓝队知识浅谈(上)
  • 测试工程师简历「精选篇」
  • sol机器人pump机器人如何实现盈利的?什么是Pump 扫链机器人?
  • redis7.x源码分析:(1) sds动态字符串
  • 学法减分交管12123模拟练习小程序源码前端和后端和搭建教程
  • 应用ANN+SMOTE+Keras Tuner算法进行信用卡交易欺诈侦测
  • JPEG图像的压缩标准(1)
  • 【蓝桥杯冲冲冲】Invasion of the Milkweed G
  • Excel——有效性、二级菜单联动
  • 【开源】JAVA+Vue+SpringBoot实现班级考勤管理系统
  • pytorch——保存‘类别名与类别数量’到权值文件中
  • python创建udf函数步骤
  • macbook电脑如何永久删除app软件?
  • java基础(2) 面向对象编程-java核心类
  • pytest+allure批量执行测试用例
  • Linux操作系统基础(三):虚拟机与Linux系统安装
  • MATLAB环境下用于提取冲击信号的几种解卷积方法
  • 致我的2023年——个人学年总结
  • 32I2C通信协议
  • android 音频调试技巧
  • 25、数据结构/二叉树相关练习20240207
  • vue项目开发vscode配置
  • 《学成在线》微服务实战项目实操笔记系列(P1~P83)【上】
  • FastAPI使用ORJSONResponse作为默认的响应类型
  • MyBatis之动态代理实现增删改查以及MyBatis-config.xml中读取DB信息文件和SQL中JavaBean别名配置