八皇后问题和数独问题是经典dfs+回溯的问题,其中还可以涉及hash,是非常好的练习题目。我们以leetcode中的四道题目为例。
Determine if a Sudoku is valid
The Sudoku board could be partially filled, where empty cells are filled with the character '.'
.
A partially filled sudoku which is valid.
Note:
A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated.
简单的判断数独表是否有解,只需要穷举每个元素判断其所在行、列以及小九宫格内没有和它相同的元素,为此我们可以建立hash表。
class Solution { public: bool isValidSudoku(vector<vector<char>>& board) { int showed_col[9][9]={0}, showed_row[9][9]={0}, showed_sub[9][9]={0}; for(int i = 0 ; i < board.size();i++){ for(int j = 0; j < board[i].size(); j++){ int num = board[i][j] - '0' -1; int sub = i/3*3+j/3; if(board[i][j] !='.'){ if(showed_col[i][num] || showed_row[num][j] || showed_sub[sub][num]) return false; showed_col[i][num] = 1; showed_row[num][j] = 1; showed_sub[sub][num] = 1; } } } return true; } };
Write a program to solve a Sudoku puzzle by filling the empty cells.
Empty cells are indicated by the character '.'
.
You may assume that there will be only one unique solution.
A sudoku puzzle...
...and its solution numbers marked in red.
求出数独的解,这里有个前提就是数独的解是唯一的,通过dfs+回溯解决。一共有两个核心函数,一个是回溯函数,一个是判断函数。
class Solution { public: void solveSudoku(vector<vector<char>>& board) { solve(board,0); } bool isValidFill(vector<vector<char>> &board, int i,int j, char fill) { for(int k = 0; k < 9; k++) { if(board[i][k] == fill) return false; if(board[k][j] == fill) return false; int r = i/3*3+j/3; if(board[r/3*3+k/3][r%3*3+k%3] == fill) return false; } return true; } bool solve(vector<vector<char>>&board, int ind) { if(ind == 81) return true; int i = ind/9, j = ind%9; if(board[i][j] !='.') return solve(board, ind+1); else{ for(char f = '1'; f<='9'; f++) { if(isValidFill(board,i,j,f)){ board[i][j] = f; if(solve(board,ind+1)) return true; board[i][j] = '.'; } } } return false; } };
下面我们会看到,八皇后问题与数独问题的求解十分相似。
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.
Given an integer n, return all distinct solutions to the n-queens puzzle.
Each solution contains a distinct board configuration of the n-queens' placement, where 'Q'
and '.'
both indicate a queen and an empty space respectively.
For example,
There exist two distinct solutions to the 4-queens puzzle:
[ [".Q..", // Solution 1 "...Q", "Q...", "..Q."], ["..Q.", // Solution 2 "Q...", "...Q", ".Q.."] ]
class Solution { public: vector<vector<string>> solveNQueens(int n) { vector<vector<string>> res; vector<string> nQueens(n, string(n,'.')); solveNQueens(res, nQueens, 0, n); return res; } bool isValid(vector<string> &nQueens, int row,int col,int &n){ for(int i=0; i != row; i++) { if(nQueens[i][col] == 'Q') return false; } for(int i = row -1, j = col -1; i >=0 && j>=0;--i,--j){ if(nQueens[i][j] == 'Q') return false; } for(int i = row -1, j = col+1;i>=0&&j>=0;--i,++j){ if(nQueens[i][j] == 'Q') return false; } return true; } void solveNQueens(vector<vector<string>> &res,vector<string> &nQueens,int row, int &n){ if(row == n) { res.push_back(nQueens); return; } for(int col = 0; col!=n;col++){ if(isValid(nQueens, row, col, n)) { nQueens[row][col] = 'Q'; solveNQueens(res, nQueens,row+1,n); nQueens[row][col] = '.'; } } } };
Follow up for N-Queens problem.
Now, instead outputting board configurations, return the total number of distinct solutions.
class Solution { public: int totalNQueens(int n) { int result = 0; vector<int> A(n,-1); NQueens(n,result,0,A); return result; } bool isValid(vector<int> &A, int r){ for(int i = 0; i < r; i++) { if((A[i] == A[r]) || (abs(A[i]-A[r]) == (r-i))) return false; } return true; } void NQueens(int n, int &result, int cur, vector<int> &A) { if(cur == n) { result++; return; }else{ for(int i = 0; i < n; i++) { A[cur] = i; if(isValid(A,cur)) NQueens(n,result,cur+1,A); } } } };