Loading

回溯+约束编程小结

回溯+约束编程小结

在刷Leetcode的时候,遇到了解数独和N皇后这两个问题,他们的解法有相类似的地方,记录一下。

解数独

题目描述

编写一个程序,通过已填充的空格来解决数独问题。

一个数独的解法需遵循如下规则:

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的3x3宫内只能出现一次。

空白格用'.' 表示。

一个数独

答案被标成红色。

Note:

  • 给定的数独序列只包含数字 1-9 和字符 '.' 。
  • 你可以假设给定的数独只有唯一解。
  • 给定数独永远是 9x9 形式的。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sudoku-solver
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

代码

class Solution {

    int n = 3;
    int N = n * n;
    //分别代表每一行、列、方块中0-9填写的数字出现的次数
    int [][] rows = new int[N][N + 1];
    int [][] columns = new int[N][N + 1];
    int [][] boxes = new int[N][N + 1];

    char[][] board=null;
    boolean sudokuSolved = false;

    public void solveSudoku(char[][] board) {
        this.board = board;
        // 初始化
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                char num = board[i][j];
                if (num != '.') {
                    int d = Character.getNumericValue(num);
                    placeNumber(d, i, j);
                }
            }
        }
        backtrack(0, 0);
    }

    public boolean couldPlace(int d, int row, int col) {
        /*
        * 检查该位置是否能放数字
        */
        int idx = (row / n ) * n + col / n;
        return rows[row][d] + columns[col][d] + boxes[idx][d] == 0;
    }
    
    public void placeNumber(int d, int row, int col) {
        /*
        Place a number d in (row, col) cell
        */
        int idx = (row / n ) * n + col / n;
    
        rows[row][d]++;
        columns[col][d]++;
        boxes[idx][d]++;
        board[row][col] = (char)(d + '0');
    }
    
    public void removeNumber(int d, int row, int col) {
        /*
        Remove a number which didn't lead to a solution
        */
        int idx = (row / n ) * n + col / n;
        rows[row][d]--;
        columns[col][d]--;
        boxes[idx][d]--;
        board[row][col] = '.';
    }
    
    public void placeNextNumbers(int row, int col) {
     
        if ((col == N - 1) && (row == N - 1)) {
          sudokuSolved = true;
        }
        // if not yet
        else {
          // if we're in the end of the row
          // go to the next row
          if (col == N - 1) backtrack(row + 1, 0);
            // go to the next column
          else backtrack(row, col + 1);
        }
    }

    public void backtrack(int row, int col) {
        /*
        Backtracking
        */
        // 如果这个位置为空
        if (board[row][col] == '.') {
          //从 0->9进行遍历
          for (int d = 1; d < 10; d++) {
            if (couldPlace(d, row, col)) {
              placeNumber(d, row, col);
              placeNextNumbers(row, col);
              // if sudoku is solved, there is no need to backtrack
              // since the single unique solution is promised
              if (!sudokuSolved) removeNumber(d, row, col);
            }
          }
        }
        else placeNextNumbers(row, col);
    }
}

解法参考:https://leetcode-cn.com/problems/sudoku-solver/solution/jie-shu-du-by-leetcode/

N皇后问题

题目描述

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

8-queens

上图为 8 皇后问题的一种解法。

给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例:

输入: 4
输出: [
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。

提示:

  • 皇后,是国际象棋中的棋子,意味着国王的妻子。皇后只做一件事,那就是“吃子”。当她遇见可以吃的棋子时,就迅速冲上去吃掉棋子。当然,她横、竖、斜都可走一到七步,可进可退。(引用自 百度百科 - 皇后 )

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-queens
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

代码

class Solution {
    //存储每一行皇后放的位置
    int rows[];
    // 主对角线
    int hills[];
    // 次对角线
    int dales[];
    int n;
    List<List<String>> output ;
    // 皇后位置,用于最后的设置
    int queens[];

    public boolean isNotUnderAttack(int row, int col) {
        int res = rows[col] + hills[row - col + 2 * n] + dales[row + col];
        return (res == 0) ? true : false;
    }

    public List<List<String>> solveNQueens(int n) {
        output  =new ArrayList<>();
        this.n = n;
        rows = new int[n];
        //主对角线大一些是为了防止相减出现出现负数
        hills = new int[4 * n - 1];
        dales = new int[2 * n - 1];
        queens = new int[n];
    
        backtrack(0);
        return output; 
    }

    public void placeQueen(int row, int col) {
        queens[row] = col;
        rows[col] = 1;
        //表示那条对角线被填
        hills[row - col + 2 * n] = 1;  // "hill" diagonals
        dales[row + col] = 1;   //"dale" diagonals
    }

    public void removeQueen(int row, int col) {
        queens[row] = 0;
        rows[col] = 0;
        hills[row - col + 2 * n] = 0;
        dales[row + col] = 0;
    }

    public void addSolution() {
        List<String> solution = new ArrayList<String>();
        for (int i = 0; i < n; ++i) {
          int col = queens[i];
          StringBuilder sb = new StringBuilder();
          for(int j = 0; j < col; ++j) sb.append(".");
          sb.append("Q");
          for(int j = 0; j < n - col - 1; ++j) sb.append(".");
          solution.add(sb.toString());
        }
        output.add(solution);
    }

    public void backtrack(int row) {
        for (int col = 0; col < n; col++) {
          if (isNotUnderAttack(row, col)) {
            placeQueen(row, col);
            // 放满了
            if (row + 1 == n) addSolution();
              // if not proceed to place the rest
            else backtrack(row + 1);
            // backtrack
            removeQueen(row, col);
          }
        }
    }
}

解题思想参考:https://leetcode-cn.com/problems/n-queens/solution/nhuang-hou-by-leetcode/

posted @ 2020-07-12 22:50  Bug_Cat  阅读(100)  评论(0)    收藏  举报