Sudoku Solver
题目连接:https://oj.leetcode.com/problems/sudoku-solver/
runtimes:15ms
一、问题
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.
二、分析
数独问题的规则是每一行,每一列,每一宫的9个格是1-9的序列,没有重复。因此传统的思路是逐一扫描,往一个格填入一个数,判断是否合法,合法就继续下一个格,不合法就继续下一个数。比如我们从左至右,从上至下扫描,遇到R1C3(第一行第三列),填入1-3发现都不符合,因此继续尝试4,发现满足要求,于是继续填入R1C4,以此类推,依次完成剩下的空格。
三、小结
以上分析是传统的回溯算法思想。回溯算法代码比较固定,以下给出非递归和递归两种方案。
其中我用了rowState、colState、palaState来记录行、列、宫的数字使用情况,用record来记录空格坐标。
四、方案
非递归方案的实现:
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
void solveSudoku(vector<vector<char> > &board) {
const int place[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
bool rowState[9][9], colState[9][9], palaState[9][9];
int record[81][2], counter = 0;
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
rowState[i][j] = colState[i][j] = palaState[i][j] = false;
}
}
for (int i = 0; i < board.size(); i++)
{
for (int j = 0; j < board[i].size(); j++)
{
if (board[i][j] != '.')
{
rowState[i][board[i][j] - '1'] = true;
colState[j][board[i][j] - '1'] = true;
palaState[place[i / 3][j / 3] - 1][board[i][j] - '1'] = true;
}
else{
record[counter][0] = i;
record[counter][1] = j;
counter++;
}
}
}
int i = 0, j = 0, k = 1;
vector <int> kVec; //kVec.push_back(k);
while (i < counter)
{
while (k < 10)
{
if (i < counter && !rowState[record[i][0]][k - 1] && !colState[record[i][1]][k - 1] && !palaState[place[record[i][0] / 3][record[i][1] / 3] - 1][k - 1])
{
board[record[i][0]][record[i][1]] = k + '0';
rowState[record[i][0]][k - 1] = true;
colState[record[i][1]][k - 1] = true;
palaState[place[record[i][0] / 3][record[i][1] / 3] - 1][k - 1] = true;
i++;
kVec.push_back(k);
k = 1;
}
else{
k++;
}
}
if (i >= counter)
{
for (int i = 0; i < board.size(); i++)
{
for (int j = 0; j < board.size(); j++)
{
cout << board[i][j] << " ";
}
cout << endl;
}
return ;
}
else{
i--; k = kVec.back(); kVec.pop_back();
board[record[i][0]][record[i][1]] = '.';
rowState[record[i][0]][k - 1] = false;
colState[record[i][1]][k - 1] = false;
palaState[place[record[i][0] / 3][record[i][1] / 3] - 1][k - 1] = false;
k++;
}
}
}
};
int main(int argc, char **argv[])
{
char b[9][9] = {
{ '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' } };
vector <vector <char> > board(9, vector<char>(9, '.'));
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
board[i][j] = b[i][j];
}
}
Solution s;
s.solveSudoku(board);
return 0;
}
递归方案的实现:
class Solution {
public:
bool DS(vector<vector<char> > &board, int i, const int place[3][3], bool rs[9][9], bool cs[9][9], bool ps[9][9], int re[81][2], int co)
{
if (i < co)
{
for (int k = 1; k < 10; k++)
{
if (!rs[re[i][0]][k - 1] && !cs[re[i][1]][k - 1] && !ps[place[re[i][0] / 3][re[i][1] / 3] - 1][k - 1])
{
board[re[i][0]][re[i][1]] = k + '0';
rs[re[i][0]][k - 1] = true;
cs[re[i][1]][k - 1] = true;
ps[place[re[i][0] / 3][re[i][1] / 3] - 1][k - 1] = true;
if (DS(board, i + 1, place, rs, cs, ps, re, co))
{
return true;
}
else{
board[re[i][0]][re[i][1]] = '.';
rs[re[i][0]][k - 1] = false;
cs[re[i][1]][k - 1] = false;
ps[place[re[i][0] / 3][re[i][1] / 3] - 1][k - 1] = false;
}
}
}
return false;
}
else{
return true;
}
}
void solveSudoku(vector<vector<char> > &board) {
const int place[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
bool rowState[9][9], colState[9][9], palaState[9][9];
int record[81][2], counter = 0;
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
rowState[i][j] = colState[i][j] = palaState[i][j] = false;
}
}
for (int i = 0; i < board.size(); i++)
{
for (int j = 0; j < board[i].size(); j++)
{
if (board[i][j] != '.')
{
rowState[i][board[i][j] - '1'] = true;
colState[j][board[i][j] - '1'] = true;
palaState[place[i / 3][j / 3] - 1][board[i][j] - '1'] = true;
}
else{
record[counter][0] = i;
record[counter][1] = j;
counter++;
}
}
}
if (DS(board, 0, place, rowState, colState, palaState, record, counter))
{
for (int i = 0; i < board.size(); i++)
{
for (int j = 0; j < board.size(); j++)
{
cout << board[i][j] << " ";
}
cout << endl;
}
return;
}
}
};
五、三思
这里有两种方法判断board是否合法,第一种就是每次填完数循环扫描行、列、宫,看是否合法,明显酱紫花时间,我的代码用空间换空间,将O(3*9)降到O(3),即更新了每次行、列、宫使用数字的情况,而不用循环扫描。
这篇文章写得还不错,可以参考:http://www.cnblogs.com/zhaolizhen/p/Sudoku.html