递归与回溯之N皇后

递归的主要特点如下:
自己调用自己
递归通常不在意具体操作,只关心初始条件和上下层的变化关系。
递归函数需要有临界停止点,即递归不能无限制的执行下去。通常这个点为必须经过的一个数。

算法界中,有五大常用算法:贪心算法、分治算法、动态规划算法、回溯算法、分支界限算法。咱们回溯算法就是五大之一,因为回溯算法能够解决很多实际的问题,尽管很多时候复杂度可能不太小,但大部分情况都能得到一个不错的结果。

对于回溯法的定义,百度百科是这么定义的:

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。

对于回溯法,它的核心就是试探和复原。这个自动化的过程就是利用递归去执行,在递归函数执行前去修改尝试,满足条件后向下递归试探,试探完毕后需要将数值复原。在这个试探的过程中找到我们所需要的一个或者所有解。这个我们也俗称暴力。

八皇后问题介绍:

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于 1848 年提出:在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、 同一列或同一斜线上,问有多少种摆法

N皇后问题来源于八皇后问题,为了简化分析,我们将其改为四皇后问题。
四皇后问题共有两种摆法:
0 1 0 0
0 0 0 1
1 0 0 0
0 0 1 0

0 0 1 0
1 0 0 0
0 0 0 1
0 1 0 0

思路分析:
1.第一个皇后先放第一行第一列,可以摆放则向下一行递归。
2.第二个皇后放在第二行第一列、然后判断是否 OK,如果OK则向下一行递归, 如果不 OK,继续放在第二列、第三列、直至把所有列都放完,看是否有OK的,有则向下一行递归,没有则向上一行回溯。
3.继续第三个皇后,还是第一列、第二列……直到第 四个皇后也能放在一个不冲突的位置,算是找到了一个正确解。如果当前行所有列都冲突,则回溯至上一行。
4.当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正确解, 全部得到
然后回头继续第一个皇后放第二列,后面继续循环执行 1,2,3,4 的步骤

public class NQueen {
    // 皇后个数,也是棋盘行列数
    public static int QUEEN = 4;
    // 初始化棋盘,二维数组,行在前列在后,记忆行列式
    public static int[][] Board = new int[QUEEN][QUEEN];
    private static int count;

    public static void main(String[] args) {
        // 从第0行开始
        findCount(0);
        System.out.println("摆放一共有" + count + "种");
    }

    public static void findCount(int row) {
        //第0列开始
        for (int col = 0; col < QUEEN; col++) {
            //检查位置是否可以放棋子
            if (checkBoard(QUEEN, row, col, Board) == 1) {
                if (row == QUEEN - 1) {
                    // 如果已经到最后一行,则打印棋盘
                    printBoard(QUEEN, Board);
                    count++;
                }
                // 递归,向下一行前进
                if (row < QUEEN - 1) {
                    findCount(++row);
                    // 回退上一个栈,向上一行回溯
                    --row;
                }
            }
            // 重新赋值为0,进行下一次判断
            Board[row][col] = 0;
        }
    }

    // 检查棋盘
    public static int checkBoard(int QUEEN, int row, int col, int[][] Board) {
        // 行方向上,因为每行只放一个元素,且行是递增的,所以不用判断
//        for (int i = 0; i < QUEEN; i++) {
//            // 判断第row行上是否存在棋子
//            if (Board[row][i] == 1) {
//                return 0;
//            }
//        }
        // 列方向上
        for (int i = 0; i < QUEEN; i++) {
            if (Board[i][col] == 1) {
                return 0;
            }
        }
        // 右下方向上,因为下一行还没有元素,所以不用判断
//        for (int i = row, j = col; i < QUEEN && j < QUEEN; i++, j++) {
//            if (Board[i][j] == 1) {
//                return 0;
//            }
//        }
        // 左上方向上
        for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {
            if (Board[i][j] == 1) {
                return 0;
            }
        }
        // 左下方向上,因为下一行还没有元素,所以不用判断
//        for (int i = row, j = col; i < QUEEN && j >= 0; i++, j--) {
//            if (Board[i][j] == 1) {
//                return 0;
//            }
//        }
        // 右上方向上
        for (int i = row, j = col; i >= 0 && j < QUEEN; i--, j++) {
            if (Board[i][j] == 1) {
                return 0;
            }
        }

        // 此点符合要求,可以下
        Board[row][col] = 1;
        // 可以放
        return 1;
    }

    // 打印棋盘
    public static void printBoard(int QUEEN, int[][] Board) {
        for (int i = 0; i < QUEEN; i++) {
            for (int j = 0; j < QUEEN; j++) {
                System.out.print(Board[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println("================");
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
N皇后问题是指在N*N的棋盘上放置N个皇后,使得皇后之间互相不能攻击(即任意两个皇后不在同一行、同一列或同一对角线上)。 解法1:归的思路是在每个位置尝试放置皇后,如果放置后不冲突,则归到下一行继续放置。如果放置后冲突,则回溯到上一行,重新选择位置。 具体实现如下: ```python def solveNQueens(n): # 初始化棋盘 board = [['.' for j in range(n)] for i in range(n)] res = [] def backtrack(row): # 找到解 if row == n: res.append([''.join(row) for row in board]) return for col in range(n): if not is_conflict(row, col): board[row][col] = 'Q' backtrack(row + 1) board[row][col] = '.' def is_conflict(row, col): # 检查列冲突 for i in range(row): if board[i][col] == 'Q': return True # 检查左上方对角线冲突 i, j = row - 1, col - 1 while i >= 0 and j >= 0: if board[i][j] == 'Q': return True i, j = i - 1, j - 1 # 检查右上方对角线冲突 i, j = row - 1, col + 1 while i >= 0 and j < n: if board[i][j] == 'Q': return True i, j = i - 1, j + 1 return False backtrack(0) return res ``` 解法2:回溯 回溯的思路是枚举每个位置的状态,如果状态合法,则归到下一层继续枚举。如果状态不合法,则回溯到上一层,重新选择状态。 具体实现如下: ```python def solveNQueens(n): # 初始化棋盘 board = [['.' for j in range(n)] for i in range(n)] res = [] def is_conflict(row, col): # 检查列冲突 for i in range(row): if board[i][col] == 'Q': return True # 检查左上方对角线冲突 i, j = row - 1, col - 1 while i >= 0 and j >= 0: if board[i][j] == 'Q': return True i, j = i - 1, j - 1 # 检查右上方对角线冲突 i, j = row - 1, col + 1 while i >= 0 and j < n: if board[i][j] == 'Q': return True i, j = i - 1, j + 1 return False def backtrack(row): # 找到解 if row == n: res.append([''.join(row) for row in board]) return for col in range(n): if not is_conflict(row, col): board[row][col] = 'Q' backtrack(row + 1) board[row][col] = '.' backtrack(0) return res ``` 两种方法本质上是一样的,只是归的写法可能更加简洁。在实际使用中,需要根据具体情况选择使用哪种方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值