编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 '.' 表示。
一个数独 答案标记为红色
Note:
给定的数独序列只包含数字 1-9 和字符 '.' 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。
class Solution {
public:
void solveSudoku(vector>& board) {
backtrack(board,0,0);
}
bool backtrack(vector>& board, int row, int col){
if(col == 9)
return backtrack(board,row+1,0);
if(row == 9)
return true;
for(int i = row; i < 9; i++){
for(int j = col; j < 9; j++){
if(board[i][j] != '.'){
return backtrack(board, i, j + 1);
}
for(char ch = '1'; ch <= '9'; ch++){
if(!isValid(board, i, j, ch))
continue;
board[i][j] = ch;
if(backtrack(board, i, j + 1))
return true;
board[i][j] = '.';
}
return false;
}
}
return false;
}
bool isValid(vector>& board, int row, int col, char ch){
for(int i = 0; i < 9; i++){
if(board[row][i] == ch) return false;
if(board[i][col] == ch) return false;
if(board[(row/3)*3 + i/3][(col/3)*3 + i%3] == ch) return false;
}
return true;
}
};
直观来讲,数独问题就是在空中填入数, 并且满足约束条件.
如果说在当前空中没有一个数满足约束条件, 肯定是之前的空填错了,那么就需要回溯.
整个思路按照回溯算法进行, 也就是说在深度搜索的过程中, 需要记录每一步填入数的过程.
所以算法如下:
void print(vector> vec) {
for (auto a : vec) {
cout << a.first << ' ' << a.second<<" | ";
}
cout << endl;
}
template
void print(vector vec) {
for (auto a : vec) {
cout << a << ' ';
}
cout << endl;
}
template
void print(vector> vec) {
for (const auto &v: vec) {
print(v);
}
cout << endl;
}
class Solution {
public:
using Point = pair;
bool check(int row, int col, char value, vector> &board) {
int grid_row = row / 3;
int grid_col = col / 3;
for (int i = grid_row * 3; i < (grid_row + 1) * 3; i++) {
for (int j = grid_col * 3; j < (grid_col + 1) * 3; j++) {
if (value == board[i][j])
return false;
}
}
for (int i = 0; i < 9; i++) {
if (value == board[i][col])
return false;
}
for (int j = 0; j < 9; j++) {
if (value == board[row][j])
return false;
}
return true;
}
Point getNext(int row, int col, vector> &board) {
Point next = {-1, -1};
if (row != 9 && col != 9) {
for (int j = col; j < 9; j++) {
if (board[row][j] == '.') return make_pair(row, j);
}
for (int i = row + 1; i < 9; i++)
for (int j = 0; j < 9; j++) {
if (board[i][j] == '.') return make_pair(i, j);
}
}
return next;
}
bool dfs(int row, int col, vector> &board, vector &stack) {
auto p = getNext(row, col, board);
if (p.first == -1) {
return true;
}
bool is_check = false;
for (char c = '1'; c <= '9'; c++) {
if (check(p.first, p.second, c, board)) {
board[p.first][p.second] = c;
stack.emplace_back(p.first, p.second);
print(stack);
print(board);
if (p.second + 1 == 9) {
is_check = dfs(p.first + 1, 0, board, stack);
} else {
is_check = dfs(p.first, p.second + 1, board, stack);
}
}
}
// 运行到这里, 就会出现没有一个数可以填入当前空, 弹栈
if (!is_check) {
auto g = stack[stack.size() - 1];
stack.pop_back();
board[g.first][g.second] = '.';
}
return is_check;
}
vector> solveSudoku(vector> &board) {
vector stack;
dfs(0, 0, board, stack);
// print(board);
return board;
}
};
class Solution {
// box size
int n = 3;
// row size
int N = n * n;
int [][] rows = new int[N][N + 1];
int [][] columns = new int[N][N + 1];
int [][] boxes = new int[N][N + 1];
char[][] board;
boolean sudokuSolved = false;
public boolean couldPlace(int d, int row, int col) {
/*
Check if one could place a number d in (row, col) cell
*/
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) {
/*
Call backtrack function in recursion
to continue to place numbers
till the moment we have a solution
*/
// if we're in the last cell
// that means we have the solution
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 the cell is empty
if (board[row][col] == '.') {
// iterate over all numbers from 1 to 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);
}
public void solveSudoku(char[][] board) {
this.board = board;
// init rows, columns and boxes
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);
}
}
from collections import defaultdict
class Solution:
def solveSudoku(self, board):
"""
:type board: List[List[str]]
:rtype: void Do not return anything, modify board in-place instead.
"""
def could_place(d, row, col):
"""
Check if one could place a number d in (row, col) cell
"""
return not (d in rows[row] or d in columns[col] or \
d in boxes[box_index(row, col)])
def place_number(d, row, col):
"""
Place a number d in (row, col) cell
"""
rows[row][d] += 1
columns[col][d] += 1
boxes[box_index(row, col)][d] += 1
board[row][col] = str(d)
def remove_number(d, row, col):
"""
Remove a number which didn't lead
to a solution
"""
del rows[row][d]
del columns[col][d]
del boxes[box_index(row, col)][d]
board[row][col] = '.'
def place_next_numbers(row, col):
"""
Call backtrack function in recursion
to continue to place numbers
till the moment we have a solution
"""
# if we're in the last cell
# that means we have the solution
if col == N - 1 and row == N - 1:
nonlocal sudoku_solved
sudoku_solved = 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)
def backtrack(row = 0, col = 0):
"""
Backtracking
"""
# if the cell is empty
if board[row][col] == '.':
# iterate over all numbers from 1 to 9
for d in range(1, 10):
if could_place(d, row, col):
place_number(d, row, col)
place_next_numbers(row, col)
# if sudoku is solved, there is no need to backtrack
# since the single unique solution is promised
if not sudoku_solved:
remove_number(d, row, col)
else:
place_next_numbers(row, col)
# box size
n = 3
# row size
N = n * n
# lambda function to compute box index
box_index = lambda row, col: (row // n ) * n + col // n
# init rows, columns and boxes
rows = [defaultdict(int) for i in range(N)]
columns = [defaultdict(int) for i in range(N)]
boxes = [defaultdict(int) for i in range(N)]
for i in range(N):
for j in range(N):
if board[i][j] != '.':
d = int(board[i][j])
place_number(d, i, j)
sudoku_solved = False
backtrack()