• 【算法-回溯法】N皇后问题


    一、问题背景

    N皇后问题是由八皇后问题引申而来的。八皇后是一个以国际象棋为背景的问题,国际象棋8*8.

    怎么去放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。

    条件n = 1或n ≥ 4

    二、算法介绍

    此题解的算法使用的是:回溯法(Backtracking)

    回溯法是暴力搜索法里的一种。其核心是通过逐步构建空间,并在构建过程中进行选择、判断和回退,直到找到问题的解或者确定不存在解。

    回溯法采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现,现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况:

    • 找到一个可能存在的正确的答案
    • 在尝试了所有可能的分步方法后宣告该问题没有答案

    回溯法的优点是可以穷尽所有可能的解空间,并找到所有满足条件的解。但同时,由于它遍历了所有可能的解空间,所以在解空间很大的情况下,会产生指数级的时间复杂度,因此效率可能较低。

    回溯法的经典应用包括:N皇后问题、组合求和、排列组合、子集、正则表达式匹配等

    三、代码示例

    1、C语言版本(ACM算法题目里面经典)

    1. #include
    2. #define N 8 // N代表棋盘的大小,这里设置为8
    3. int board[N][N]; // 棋盘数组,用于表示每个位置是否放置皇后
    4. // 检查当前位置(row, col)是否可以放置皇后
    5. int isSafe(int row, int col) {
    6. int i, j;
    7. // 检查当前列是否有皇后冲突
    8. for (i = 0; i < row; i++) {
    9. if (board[i][col] == 1) {
    10. return 0;
    11. }
    12. }
    13. // 检查当前位置的左上方是否有皇后冲突
    14. for (i = row, j = col; i >= 0 && j >= 0; i--, j--) {
    15. if (board[i][j] == 1) {
    16. return 0;
    17. }
    18. }
    19. // 检查当前位置的右上方是否有皇后冲突
    20. for (i = row, j = col; i >= 0 && j < N; i--, j++) {
    21. if (board[i][j] == 1) {
    22. return 0;
    23. }
    24. }
    25. return 1; // 当前位置可以放置皇后
    26. }
    27. // 使用回溯法解决N皇后问题
    28. int solveNQueens(int row) {
    29. if (row == N) {
    30. // 打印解
    31. for (int i = 0; i < N; i++) {
    32. for (int j = 0; j < N; j++) {
    33. printf("%d ", board[i][j]);
    34. }
    35. printf("\n");
    36. }
    37. printf("\n");
    38. // 只打印一个解的话,可以使用 return 1; 终止程序
    39. // return 1;
    40. }
    41. for (int col = 0; col < N; col++) {
    42. if (isSafe(row, col)) {
    43. // 当前位置可以放置皇后
    44. board[row][col] = 1;
    45. // 递归调用解决下一行
    46. solveNQueens(row + 1);
    47. // 回溯,撤销当前位置的选择
    48. board[row][col] = 0;
    49. }
    50. }
    51. return 0; // 所有解都已找到
    52. }
    53. int main() {
    54. solveNQueens(0); // 从第一行开始解决N皇后问题
    55. return 0;
    56. }

    2、JS版本 (主要是思想)

    1. function solveNQueens(n) {
    2. // 存储结果的数组
    3. let result = [];
    4. // 执行回溯算法
    5. backtrack([], [], [], result, n);
    6. // 返回结果数组
    7. return result;
    8. }
    9. // 回溯函数
    10. function backtrack(board, cols, diagonals1, diagonals2, n) {
    11. // 当前棋盘的大小等于N时,表示已找到一种解法,将其存入结果数组
    12. if (board.length === n) {
    13. result.push(board.slice());
    14. return;
    15. }
    16. // 遍历当前行的每一列
    17. for (let col = 0; col < n; col++) {
    18. // 当前格子的行索引
    19. let row = board.length;
    20. // 根据皇后的摆放规则判断当前位置是否可行
    21. if (
    22. !cols.includes(col) && // 列上无冲突
    23. !diagonals1.includes(row - col) && // 主对角线无冲突
    24. !diagonals2.includes(row + col) // 副对角线无冲突
    25. ) {
    26. // 将当前位置加入相应的集合中,表示皇后占据了该位置
    27. board.push(col);
    28. cols.push(col);
    29. diagonals1.push(row - col);
    30. diagonals2.push(row + col);
    31. // 继续递归搜索下一行
    32. backtrack(board, cols, diagonals1, diagonals2, n);
    33. // 搜索完之后,将当前位置从集合中移除,以便进行下一次搜索
    34. board.pop();
    35. cols.pop();
    36. diagonals1.pop();
    37. diagonals2.pop();
    38. }
    39. }
    40. }
    41. // 通过维护cols、diagonals1和diagonals2三个集合,来判断当前位置是否符合皇后的放置规则。
    42. // 如果找到一种解法,将其存入结果数组中,并继续搜索下一行的解法。
    43. // 在搜索完一条路径之后,需要将当前位置从集合中移除,以便进行下一次搜索。

    四、结果

    八皇后:92个解

    如果将旋转和对称的解归为一种的话,则一共有12个独立解,具体如下:

    四皇后:2个解

    (1,3,0,2)

    (2,0,3,1)

  • 相关阅读:
    Simulink建模之键盘快捷方式和鼠标操作
    k8s在线安装harbor镜像仓库
    JSD-2204-(业务逻辑开发)-续秒杀业务-消息队列-Day14
    一站到底,Spring+SpringBoot+SpringCloud全攻略,是真的全面
    算法训练——单调栈专题
    LC 239.滑动窗口最大值
    【机器学习】模型训练:线性模型的公式法与三种梯度下降法求解
    uni-app下Worker的使用
    关键路径的分析
    http和https包解析
  • 原文地址:https://blog.csdn.net/weixin_40571965/article/details/133803791