回溯专题其五(棋盘篇)

一,棋盘类问题解题思路

棋盘类问题是回溯专题中的一个重要分支。与子集、组合、排列类问题不同,棋盘类问题往往需要满足额外的约束条件,即在棋盘上某个位置放置元素时,必须确保不会破坏题目定义的规则。

常见的棋盘类问题包括:

  • N皇后:放置皇后时,不能在同一行、同一列或对角线上出现冲突。
  • 数独:填充数字时,必须满足行、列、3x3 小方格内不能重复。

棋盘类问题的核心解题框架通常为:

  1. 棋盘遍历

    • 按照一定的顺序(逐行逐列 / 遍历空格列表)尝试放置元素。
  2. 合法性判断(剪枝)

    • 在放置元素前,检查是否满足题目条件(如行/列/对角线是否安全,数独是否冲突)。
    • 剪枝是棋盘类问题效率的关键。
  3. 递归 + 回溯

    • 如果当前位置合法,则尝试放置元素并进入下一步递归;
    • 若递归失败,撤销本次选择(回溯),继续尝试其他可能性。
  4. 优化手段

    • 提前记录状态(如用集合或数组存储当前行/列/方格中的元素),避免每次重复遍历。
    • 排序搜索空间(如数独优先填候选数最少的格子),减少搜索分支。

二,具体代码实现

Leetcode题单:

N皇后 51
解数独 37

1. N皇后 51

N皇后问题即为在一个 n*n 的棋盘上放置n个皇后,使得这些皇后不会彼此攻击。每一个皇后的攻击范围为:

  • 横向
  • 纵向
  • 左上到右下
  • 右上到左下

给定一个整数n,要求返回n皇后问题的所有解。棋盘中用 Q 代表皇后,用 . 代表空位。

思路:

  • 使用回溯逐行放置皇后;
  • 每放置一个皇后,先检查当前列和两个对角线是否符合要求;
  • 回溯时撤销当前选择,继续尝试下一列。
  • 当放置完所有行时,将当前棋盘加入结果集;

具体代码实现:

class Solution:
    def isValid(self, n: int, row: int, col: int, board: List[str]) -> bool:
        # 检验同一列上是否已经有皇后存在
        for i in range(row):
            if board[i][col] == 'Q':
                return False

        # 对角线
        i, j, k, l = row-1, col-1, row-1, col+1
        while i>=0 and j>=0:
            if board[i][j] == 'Q':
                return False
            i-=1
            j-=1

        while k>=0 and l<n:
            if board[k][l] == 'Q':
                return False
            k-=1
            l+=1

        return True

    def backtracking(self, n: int, row: int, board: List[str]) -> None:
        if row == n:
            self.result.append(board.copy())
            return

        for col in range(n):
            # 先对当前位置进行判断,只有当前位置可以放置皇后时才放置
            if not self.isValid(n, row, col, board):
                continue
            board[row] = board[row][:col] + 'Q' + board[row][col+1:]
            self.backtracking(n, row+1, board)
            board[row] = board[row][:col] + '.' + board[row][col+1:]

    def solveNQueens(self, n: int) -> List[List[str]]:
        self.result = []
        board = ['.' * n for _ in range(n)]
        self.backtracking(n, 0, board)
        return self.result

2. 解数独 37

给定一个数独棋盘,要求填充其中的所有空格。

思路:

  • 使用回溯逐个空格尝试填入数字。
  • 通过 rowscolssquares 三个集合提前记录状态,避免重复扫描整个棋盘。
  • 对空格列表进行一次静态排序,优先填候选数较少的格子,提高效率。

具体代码实现:

class Solution:
    def isValid(self, row: int, col: int, num: str) -> bool:
        # 同一行,同一列,3x3方格内不能出现重复数字
        if num in self.rows[row] or num in self.cols[col] or num in self.squares[(row // 3) * 3 + col // 3]:
            return False
        return True

    def backtracking(self, board: List[List[str]], idx: int) -> bool:
        # 当处理完所有空格时,终止递归
        if idx == len(self.empties):
            return True

        row, col = self.empties[idx]
        square = (row//3) * 3 + (col // 3)

        for candidate in map(str, range(1, 10)):
            if self.isValid(row, col, candidate):
                board[row][col] = candidate
                self.rows[row].add(candidate)
                self.cols[col].add(candidate)
                self.squares[square].add(candidate)

                if self.backtracking(board, idx+1):
                    return True

                board[row][col] = '.'
                self.rows[row].remove(candidate)
                self.cols[col].remove(candidate)
                self.squares[square].remove(candidate)

        return False

    def solveSudoku(self, board: List[List[str]]) -> None:
        self.rows = [set() for _ in range(9)]
        self.cols = [set() for _ in range(9)]
        self.squares = [set() for _ in range(9)]
        self.empties = []

        # 初始化棋盘
        for i in range(9):
            for j in range(9):
                if board[i][j] == '.':
                    self.empties.append((i, j))
                else:
                    val = board[i][j]
                    self.rows[i].add(val)
                    self.cols[j].add(val)
                    self.squares[(i // 3)*3 + j // 3].add(val)

        # 可选,对empties做一次简单的静态排序,优先处理候选填入元素较少的格子
        def candidateSort(pos):
            row, col = pos
            count = 0
            for candidate in map(str, range(1, 10)):
                if self.isValid(row, col, candidate):
                    count += 1
            return count

        self.empties.sort(key=candidateSort)

        self.backtracking(board, 0)

三,小结

棋盘类问题的本质是 在二维搜索空间中满足特定的约束条件
与子集、组合、排列类问题相比,棋盘类问题更强调 合法性判断剪枝策略

  • N皇后 中,核心是“行列对角线不能冲突”,通过逐行放置 + 剪枝实现。
  • 数独 中,核心是“行/列/方格三重约束”,通过状态记录 + 空格排序提高效率。

可以看到,虽然棋盘类问题表面复杂,但其背后依然遵循选择 -> 验证 -> 递归 -> 回溯的通用框架。

posted @ 2025-09-02 18:02  雪痕春风天音九重色  阅读(10)  评论(0)    收藏  举报