Backtrack_1_N皇后和解数独

  1. 37.解数独
  2. 51.N 皇后
  3. 52.N皇后II

37.解数独

编写一个程序,通过填充空格来解决数独问题。数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

示例 1:
输入:board = [
[“5”,”3”,”.”,”.”,”7”,”.”,”.”,”.”,”.”],
[“6”,”.”,”.”,”1”,”9”,”5”,”.”,”.”,”.”],
[“.”,”9”,”8”,”.”,”.”,”.”,”.”,”6”,”.”],
[“8”,”.”,”.”,”.”,”6”,”.”,”.”,”.”,”3”],
[“4”,”.”,”.”,”8”,”.”,”3”,”.”,”.”,”1”],
[“7”,”.”,”.”,”.”,”2”,”.”,”.”,”.”,”6”],
[“.”,”6”,”.”,”.”,”.”,”.”,”2”,”8”,”.”],
[“.”,”.”,”.”,”4”,”1”,”9”,”.”,”.”,”5”],
[“.”,”.”,”.”,”.”,”8”,”.”,”.”,”7”,”9”]
]
输出:[
[“5”,”3”,”4”,”6”,”7”,”8”,”9”,”1”,”2”],
[“6”,”7”,”2”,”1”,”9”,”5”,”3”,”4”,”8”],
[“1”,”9”,”8”,”3”,”4”,”2”,”5”,”6”,”7”],
[“8”,”5”,”9”,”7”,”6”,”1”,”4”,”2”,”3”],
[“4”,”2”,”6”,”8”,”5”,”3”,”7”,”9”,”1”],
[“7”,”1”,”3”,”9”,”2”,”4”,”8”,”5”,”6”],
[“9”,”6”,”1”,”5”,”3”,”7”,”2”,”8”,”4”],
[“2”,”8”,”7”,”4”,”1”,”9”,”6”,”3”,”5”],
[“3”,”4”,”5”,”2”,”8”,”6”,”1”,”7”,”9”]
] 解释:输入的数独如上图所示,唯一有效的解决方案如所示:

分析:
1.对每个位置进行穷举放置操作, 并回溯操作, 直到满足判断条件
2.判断每个位置是否能放入, 需要同时满足满足数独的三个条件, 同行同列的比较容易判断是否有重复
3.对于判断九宫格里面是否有重复, 假设我们当前的位置是 (i,j), 我们首先要找到当前存在在哪个九宫格里面, 也就是需要找到当前所处于的九宫格的左上角的坐标, 所有左上角的横坐标是 (0,3,6) 集合, 所有左上角的纵坐标是 (0,3,6) 集合
假设当前位置是 (1,4), 它的左上角是 (0,3), 大九宫格是 (0,1), 可以对横坐标/3 找到处于是大九宫格的哪一行, 纵坐标/3找到处于大九宫格的哪一列; 然后再对大九宫格的坐标乘以3得到它的左上角(0,3)
4.因此对于 (i,j) 来说, 左上角的坐标为 (i / 3 3, j / 3 3)
5.dfs 有个需要处理的地方是, 需要手动确认递归结束的条件是完成了数独的状态, 结束递归, 不然搜索会跳出不来; 一种避免手去更改递归状态的方法, 最好将 dfs 设计成带 bool 返回值标记是否找到结果

// 改写 dfs 为 bool 返回值标记是否找到了可行解的写法
class Solution {
 public:
  bool check(int i, int j, vector<vector<char>>& board) {
    for (int r = 0; r < 9; ++r) {
      if (i != r && board[i][j] == board[r][j]) {
        return false;
      }
    }
    for (int c = 0; c < 9; ++c) {
      if (j != c && board[i][j] == board[i][c]) {
        return false;
      }
    }
    int startRow = (i / 3) * 3;
    int startCol = (j / 3) * 3;
    for (int r = startRow; r < startRow+3; ++r) {
      for (int c = startCol; c < startCol+3; ++c) {
        if ((i != r || j != c) && board[i][j] == board[r][c])
          return false;
      }
    }
    return true;
  }

  bool dfs(int r, int c, vector<vector<char>>& board) {
    if (r >= 9) {
      return true;
    }
    if (c >= 9) {
      return dfs(r+1, 0, board);
    }
    if (board[r][c] == '.') {
      for (int i = 1; i <= 9; ++i) {
        board[r][c] = (char) (i + '0');
        if (check(r, c, board)) {
          if (dfs(r, c+1, board)) {
            return true;
          }
        }
        board[r][c] = '.';
      }
    } else {
      return dfs(r, c+1, board);
    }
    return false;
  }

  void solveSudoku(vector<vector<char>>& board) {
    if (board.empty() || board[0].empty())
      return;
    dfs(0, 0, board);
    return;
  }
};

原始 void 返回值的写法: 需要增加 finished 标签

// 原始 void 返回值的写法: 需要增加finished标签
class Solution {
 public:
  bool finished = false;
  bool check(int i, int j, vector<vector<char>>& board) {
    for (int r = 0; r < 9; ++r) {
      if (i != r && board[i][j] == board[r][j]) {
        return false;
      }
    }
    for (int c = 0; c < 9; ++c) {
      if (j != c && board[i][j] == board[i][c]) {
        return false;
      }
    }
    int startRow = (i / 3) * 3;
    int startCol = (j / 3) * 3;
    for (int r = startRow; r < startRow+3; ++r) {
      for (int c = startCol; c < startCol+3; ++c) {
        if ((i != r || j != c) && board[i][j] == board[r][c])
          return false;
      }
    }
    return true;
  }

  void dfs(int r, int c, vector<vector<char>>& board) {
    if (r >= 9) {
      finished = true;
      return;
    }
    if (c >= 9) {
      dfs(r+1, 0, board);
      return;
    }
    if (board[r][c] == '.') {
      for (int i = 1; i <= 9; ++i) {
        board[r][c] = (char) (i + '0');
        if (check(r, c, board)) {
          dfs(r, c+1, board);
          if (finished)
            return;
        }
        board[r][c] = '.';
      }
    } else {
      dfs(r, c+1, board);
    }
    return;
  }

  void solveSudoku(vector<vector<char>>& board) {
    if (board.empty() || board[0].empty())
      return;
    dfs(0, 0, board);
    return;
  }
};

51.N 皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

示例 1:输入:n = 4 输出:[
[“.Q..”,”…Q”,”Q…”,”..Q.”],
[“..Q.”,”Q…”,”…Q”,”.Q..”]
] 解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:输入:n = 1 输出:[
[“Q”]
]

分析:
1.初始化一个默认的棋盘, 因为根据规则, 每行至多放 1 个皇后, 所以在 dfs 放置的时候, 对 [行] 这个维度进行 dfs(row), 放完了直接进入下一行; dfs(row) 的内部, 对所有的列的位置 (row, col) 进行模拟放置并回溯
2.放置的时候, 需要检查放置的位置 [i,j] 是满足合法性的, 因此需要在放置之前先检查放置是合法的, 写一个 checkLegality()
3.checkLegality() 需要依次检查面的行之前不能放过, 左上方之前不能放过, 右上方之前不能放过

class Solution {
 private:
  vector<vector<string>> ans;
 public:
  bool checkLegality(int r, int c, int n, vector<string>& board) {
    // 上面的行之前不能放过
    for (int i = 0; i < r; ++i) {
      if (board[i][c] == 'Q') {
        return false;
      }
    }
    // 左上方之前不能放过
    for (int i = r-1, j = c-1; i >= 0 && j >= 0; --i, --j) {
      if (board[i][j] == 'Q') {
        return false;
      }
    }
    // 右上方之前不能放过
    for (int i = r-1, j = c+1; i >= 0 && j < n; --i, ++j) {
      if (board[i][j] == 'Q') {
        return false;
      }
    }
    return true;
  }
  void dfs(int row, int n, vector<string>& board) {
    if (row == n) {
      ans.push_back(board);
      return;
    }
    for (int col = 0; col < n; ++col) {
      if (checkLegality(row, col, n, board)) {
        board[row][col] = 'Q';
        dfs(row+1, n, board);
        board[row][col] = '.';
      }
    }
    return;
  }
  vector<vector<string>> solveNQueens(int n) {
    vector<string> board(n, string(n, '.'));
    dfs(0, n, board);
    return ans;
  }
};

52.N皇后II

n 皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。

示例 1:输入:n = 4 输出:2 解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:输入:n = 1 输出:1

分析:
1.基于皇后 dfs 回溯方法, 我们得到了 N 皇后的数量.

class Solution {
 private:
  vector<vector<string>> res;
  int count = 0;
 public:
  bool isValid(int row, int col, int n, vector<string>& board) {
    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 < n; --i, ++j) {
      if (board[i][j] == 'Q') {
        return false;
      }
    }
    return true;
  }
  void backtracking(int row, int n, vector<string>& board) {
    if (row == n) {
      ++count;
      return;
    }
    for (int col = 0; col < n; ++col) {
      if (isValid(row, col, n, board)) {
        board[row][col] = 'Q';
        backtracking(row+1, n, board);
        board[row][col] = '.';
      }
    }
    return;
  }
  int totalNQueens(int n) {
    vector<string> board(n, string(n, '.'));
    backtracking(0, n, board);
    return count; 
  }
};

转载请注明来源, from goldandrabbit.github.io

💰

×

Help us with donation