1. 题目
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.
https://leetcode.com/problems...
2. 思路
每次找到最合适填入点进行试探。
每次选择行、列、方三厢里已填充字符最多的空白点上试探。
试探时从1-9试探在当前点是否可行,如果可行,则填入并递归继续。递归成功则完全成功。递归失败则回退试探下一个字符。
如果全部填满了就直接成功。
3. 代码
耗时:49ms
#include <utility>
struct Sudoku {
// 行 列 九宫格 的一共3*9个9维向量
char lines[27][9];
char lz[27];
int total; // 总的已填充数量
};
struct Pos {
int k1, k2;
int i[2];
int j[2];
};
//int R = 0;
class Solution {
public:
void solveSudoku(vector<vector<char>>& board) {
Sudoku sdk;
init(sdk);
trans(board, sdk);
if (trySudoku(sdk)) {
for (int i = 0; i < 9; i++) {
vector<char>& v = board[i];
for (int j = 0; j < 9; j++) {
v[j] = sdk.lines[i][j];
}
}
}
//cout << "R=" << R << endl;
return;
}
void init(Sudoku& sdk) {
sdk.total = 0;
for (int i = 0; i < 27; i++) {
sdk.lz[i] = 0;
for (int j = 0; j < 9; j++) {
sdk.lines[i][j] = '.';
}
}
}
bool trySudoku(Sudoku& sdk) {
//R++;
if (sdk.total == 81) {
return true;
}
{
pair<int, int> ij = optimal9_2(sdk);
if (ij.first == -1 || ij.second == -1 || sdk.lines[ij.first][ij.second] != '.') { return false; }
int i = ij.first;
int j = ij.second;
Pos p; p.k1 = i; p.k2 = j;
calc(p);
/*cout << "[POS Try] total=" << sdk.total << " R="<< R << " ["
<< p.k1 << ", " << p.k2 << "], ["
<< p.i[0] << ", " << p.j[0] << "], ["
<< p.i[1] << ", " << p.j[1] << "]\n";
*/
char ch = '1';
for (; ch <= '9'; ch++) {
int jc = 0;
for (; jc < 9; jc++) {
if (sdk.lines[p.k1][jc] == ch
|| sdk.lines[p.i[0]][jc] == ch
|| sdk.lines[p.i[1]][jc] == ch) {
break;
}
}
if (jc < 9) { continue; }
//cout << "[NUM Try] i=" << i << " j=" << j << " ch=" << ch << endl;
sdk.lines[p.k1][p.k2] = ch;
sdk.lines[p.i[0]][p.j[0]] = ch;
sdk.lines[p.i[1]][p.j[1]] = ch;
sdk.total++;
sdk.lz[p.k1]++;
sdk.lz[p.i[0]]++;
sdk.lz[p.i[1]]++;
if (!trySudoku(sdk)) {
//cout << "[RBK try] i=" << i << " j=" << j << " ch=" << ch << endl;
sdk.lines[p.k1][p.k2] = '.';
sdk.lines[p.i[0]][p.j[0]] = '.';
sdk.lines[p.i[1]][p.j[1]] = '.';
sdk.total--;
sdk.lz[p.k1]--;
sdk.lz[p.i[0]]--;
sdk.lz[p.i[1]]--;
continue;
} else {
return true;
}
}
if (ch > '9') { return false; }
}
return false;
}
// 根据p的已有的一个(k1, k2)坐标点,计算出另外两个坐标
void calc(Pos& p) {
if (p.k1 < 9) {
// 行 -》 列和方
p.i[0] = 9 + p.k2;
p.j[0] = p.k1;
p.i[1] = 18 + (p.k1/3)*3 + p.k2/3;
p.j[1] = (p.k1%3)*3 + p.k2%3;
} else if (p.k1 < 18) {
// 列 -> 行和方
p.i[0] = p.k2;
p.j[0] = p.k1 - 9;
p.i[1] = (p.i[0]/3)*3 + p.j[0]/3;
p.j[1] = 18 + (p.i[0]%3)*3 + p.j[0]%3;
} else {
// 方 -》 行和列
p.i[0] = (p.k1/3)*3 + p.k2/3;
p.j[0] = (p.k1%3)*3 + p.k2%3;
p.i[1] = p.j[0];
p.j[1] = 9 + p.i[0];
}
}
// 找到已填充过最长的line进行下一步填充
int optimal9(Sudoku& sdk) {
int f = -1;
for (int i = 0; i < 27; i++) {
if (sdk.lz[i] != 9 && sdk.lz[i] > f) {
f = sdk.lz[i];
}
}
return f;
}
pair<int, int> optimal9_2(Sudoku& sdk) {
int f = -1;
pair<int, int> p(-1, -1);
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (sdk.lines[i][j] != '.') { continue; }
int n1 = sdk.lz[i];
int n2 = sdk.lz[9+j];
int n3 = sdk.lz[18+(i/3)*3+j/3];
if (n1 == 8 || n2 == 8 || n3 == 8) {
return make_pair(i, j);
}
int cf = n1*n1 + n2*n2 + n3*n3;
//int cf = max3(n1, n2, n3);
//int cf = n1 + n2 + n3;
if (cf > f) {
cf = f;
p = make_pair(i, j);
}
}
}
return p;
}
int max3(int i, int j , int k) {
return max(max(i, j), k);
}
void trans(vector<vector<char>>& board, Sudoku& sdk) {
for (int i = 0; i < board.size(); i++) {
vector<char>& v = board[i];
for (int j = 0; j < v.size(); j++) {
char ch = v[j];
bool dot = (ch == '.');
sdk.lines[i][j] = ch;
sdk.lines[9+j][i] = ch;
int k1 = (i/3)*3 + j/3;
int k2 = (i%3)*3 + j%3;
sdk.lines[18+k1][k2] = ch;
if (!dot) {
sdk.lz[i]++;
sdk.lz[9+j]++;
sdk.lz[18+k1]++;
sdk.total++;
}
}
}
}
};
4. 代码2
耗时:66ms, 加入exist表
#include <utility>
struct Sudoku {
// 行 列 九宫格 的一共3*9个9维向量
char lines[27][9];
char lz[27];
bool exist[27][9];
int total; // 总的已填充数量
};
struct Pos {
int k1, k2;
int i[2];
int j[2];
};
//int R = 0;
class Solution {
public:
void solveSudoku(vector<vector<char>>& board) {
Sudoku sdk;
init(sdk);
trans(board, sdk);
if (trySudoku(sdk)) {
for (int i = 0; i < 9; i++) {
vector<char>& v = board[i];
for (int j = 0; j < 9; j++) {
v[j] = sdk.lines[i][j];
}
}
}
//cout << "R=" << R << endl;
return;
}
void init(Sudoku& sdk) {
sdk.total = 0;
for (int i = 0; i < 27; i++) {
sdk.lz[i] = 0;
for (int j = 0; j < 9; j++) {
sdk.lines[i][j] = '.';
sdk.exist[i][j] = false;
}
}
}
bool trySudoku(Sudoku& sdk) {
//R++;
if (sdk.total == 81) {
return true;
}
{
pair<int, int> ij = optimal9_2(sdk);
if (ij.first == -1 || ij.second == -1 || sdk.lines[ij.first][ij.second] != '.') { return false; }
int i = ij.first;
int j = ij.second;
Pos p; p.k1 = i; p.k2 = j;
calc(p);
/*cout << "[POS Try] total=" << sdk.total << " R="<< R << " ["
<< p.k1 << ", " << p.k2 << "], ["
<< p.i[0] << ", " << p.j[0] << "], ["
<< p.i[1] << ", " << p.j[1] << "]\n";
*/
char ch = '1';
for (; ch <= '9'; ch++) {
/*int jc = 0;
for (; jc < 9; jc++) {
if (sdk.lines[p.k1][jc] == ch
|| sdk.lines[p.i[0]][jc] == ch
|| sdk.lines[p.i[1]][jc] == ch) {
break;
}
}
if (jc < 9) { continue; }
*/
int cv = ch - '1';
if (sdk.exist[p.k1][cv] || sdk.exist[p.i[0]][cv] || sdk.exist[p.i[1]][cv]) { continue; }
//cout << "[NUM Try] i=" << i << " j=" << j << " ch=" << ch << endl;
sdk.lines[p.k1][p.k2] = ch;
sdk.lines[p.i[0]][p.j[0]] = ch;
sdk.lines[p.i[1]][p.j[1]] = ch;
sdk.total++;
sdk.lz[p.k1]++;
sdk.lz[p.i[0]]++;
sdk.lz[p.i[1]]++;
sdk.exist[p.k1][cv] = sdk.exist[p.i[0]][cv] = sdk.exist[p.i[1]][cv] = true;
if (!trySudoku(sdk)) {
//cout << "[RBK try] i=" << i << " j=" << j << " ch=" << ch << endl;
sdk.lines[p.k1][p.k2] = '.';
sdk.lines[p.i[0]][p.j[0]] = '.';
sdk.lines[p.i[1]][p.j[1]] = '.';
sdk.total--;
sdk.lz[p.k1]--;
sdk.lz[p.i[0]]--;
sdk.lz[p.i[1]]--;
sdk.exist[p.k1][cv] = sdk.exist[p.i[0]][cv] = sdk.exist[p.i[1]][cv] = false;
continue;
} else {
return true;
}
}
if (ch > '9') { return false; }
}
return false;
}
// 根据p的已有的一个(k1, k2)坐标点,计算出另外两个坐标
void calc(Pos& p) {
if (p.k1 < 9) {
// 行 -》 列和方
p.i[0] = 9 + p.k2;
p.j[0] = p.k1;
p.i[1] = 18 + (p.k1/3)*3 + p.k2/3;
p.j[1] = (p.k1%3)*3 + p.k2%3;
} else if (p.k1 < 18) {
// 列 -> 行和方
p.i[0] = p.k2;
p.j[0] = p.k1 - 9;
p.i[1] = (p.i[0]/3)*3 + p.j[0]/3;
p.j[1] = 18 + (p.i[0]%3)*3 + p.j[0]%3;
} else {
// 方 -》 行和列
p.i[0] = (p.k1/3)*3 + p.k2/3;
p.j[0] = (p.k1%3)*3 + p.k2%3;
p.i[1] = p.j[0];
p.j[1] = 9 + p.i[0];
}
}
// 找到已填充过最长的line进行下一步填充
int optimal9(Sudoku& sdk) {
int f = -1;
for (int i = 0; i < 27; i++) {
if (sdk.lz[i] != 9 && sdk.lz[i] > f) {
f = sdk.lz[i];
}
}
return f;
}
pair<int, int> optimal9_2(Sudoku& sdk) {
int f = -1;
pair<int, int> p(-1, -1);
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (sdk.lines[i][j] != '.') { continue; }
int n1 = sdk.lz[i];
int n2 = sdk.lz[9+j];
int n3 = sdk.lz[18+(i/3)*3+j/3];
if (n1 == 8 || n2 == 8 || n3 == 8) {
return make_pair(i, j);
}
int cf = n1*n1 + n2*n2 + n3*n3;
//int cf = max3(n1, n2, n3);
//int cf = n1 + n2 + n3;
if (cf > f) {
cf = f;
p = make_pair(i, j);
}
}
}
return p;
}
int max3(int i, int j , int k) {
return max(max(i, j), k);
}
void trans(vector<vector<char>>& board, Sudoku& sdk) {
for (int i = 0; i < board.size(); i++) {
vector<char>& v = board[i];
for (int j = 0; j < v.size(); j++) {
char ch = v[j];
bool dot = (ch == '.');
sdk.lines[i][j] = ch;
sdk.lines[9+j][i] = ch;
int k1 = (i/3)*3 + j/3;
int k2 = (i%3)*3 + j%3;
sdk.lines[18+k1][k2] = ch;
if (!dot) {
sdk.lz[i]++;
sdk.lz[9+j]++;
sdk.lz[18+k1]++;
sdk.total++;
int cv = ch - '1';
sdk.exist[i][cv] = sdk.exist[9+j][cv] = sdk.exist[18+k1][cv] = true;
}
}
}
}
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。