Sudoku Solver 题解
题目描述
即求解数独游戏。
数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。1
如:上图中则有一个例子。
题解
这题出现在算法类题目里我还是很惊讶的,不过看到了,就顺手做了下。
利用唯一余数法,即用格位去找唯一可填数字,格位唯一可填数字称为唯余解(Naked Single)。若找不到唯余解,则随意选择一个数字进行深度搜索,并记录当前状态以便回溯。时间复杂度很高,应该是O(b^d)
。b
为可选解,d
为盘面的空位数。但由于存在唯余解,大多数情况下b = 1
,所以很快就能得到结果。
代码
#define n 9
#define POS2(i, j) ((i) * n + (j))
#define POS3(i, j) ((i) * nsquare + (j))
#define POS(i, j, k) (POS3(i, POS2(j, k)))
#define COMPOSE(i, j) (((i) << 16) | (j))
#define VAIN ('.') // 46
char* used;
class Solution {
public:
bool operator()(int a, int b) const {
int va = used[POS2(a >> 16, a & 0xFFFF)], vb = used[POS2(b >> 16, b & 0xFFFF)];
return va > vb || (va == vb && a < b);
}
void solveSudoku(vector<vector<char>>& board) {
if (board.empty() || board.size() != board[0].size())
return;
// n = board.size();
int nsqrt = sqrt(n), nsquare = n * n, nm = nsquare * (n + 1) * sizeof(char);
used = (char*)malloc(nm);
memset(used, 0, nm);
for (int i = n; i--; ) {
vector<char>& vi = board[i];
for (int j = n; j--; ) {
if (vi[j] != VAIN) {
int ct = vi[j] - '0';
int row = i / nsqrt * nsqrt, col = j / nsqrt * nsqrt;
for (int k = row + nsqrt; --k >= row; )
for (int kk = col + nsqrt; --kk >= col; ) {
int t2 = POS2(k, kk), t3 = POS3(ct, t2);
if (used[t3])
continue;
used[t3] = 1;
++used[t2];
}
for (int k = 0; k < row; ++k) {
int t2 = POS2(k, j), t3 = POS3(ct, t2);
if (used[t3])
continue;
used[t3] = 1;
++used[t2];
}
for (int k = row + nsqrt; k < n; ++k) {
int t2 = POS2(k, j), t3 = POS3(ct, t2);
if (used[t3])
continue;
used[t3] = 1;
++used[t2];
}
for (int k = 0; k < col; ++k) {
int t2 = POS2(i, k), t3 = POS3(ct, t2);
if (used[t3])
continue;
used[t3] = 1;
++used[t2];
}
for (int k = col + nsqrt; k < n; ++k) {
int t2 = POS2(i, k), t3 = POS3(ct, t2);
if (used[t3])
continue;
used[t3] = 1;
++used[t2];
}
}
}
}
std::set<int, Solution> next;
for (int i = n; i--; ) {
vector<char>& vi = board[i];
for (int j = n; j--; )
if (vi[j] == VAIN)
next.insert(COMPOSE(i, j));
else
used[POS2(i, j)] = 9;
}
std::vector<std::pair<int, char*>> stack;
NextLoop:
for (std::set<int, Solution>::iterator it = next.begin(); it != next.end(); it = next.begin()) {
int ct = 0, i = *it >> 16, j = *it & 0xFFFF, t2 = POS2(i, j);
if (used[t2] > 8) {
GoBack:
free(used);
used = stack.back().second;
ct = stack.back().first;
stack.pop_back();
next.clear();
for (int i = n; i--; )
for (int j = n; j--; )
if (used[POS2(i, j)] != 9) {
next.insert(COMPOSE(i, j));
board[i][j] = VAIN;
}
it = next.begin();
i = *it >> 16; j = *it & 0xFFFF; t2 = POS2(i, j);
}
while (++ct <= n)
if (!used[POS3(ct, t2)]) {
if (used[t2] < 8) {
char* tmp = (char*)malloc(nm);
memcpy(tmp, used, nm);
stack.push_back(std::pair<int, char*>(ct, tmp));
}
board[i][j] = ct + '0';
used[t2] = 9;
next.erase(it);
int row = i / nsqrt * nsqrt, col = j / nsqrt * nsqrt;
for (int k = row + nsqrt; --k >= row; )
for (int kk = col + nsqrt; --kk >= col; ) {
int t2 = POS2(k, kk), t3 = POS3(ct, t2);
if (used[t2] == 9 || used[t3])
continue;
next.erase(COMPOSE(k, kk));
used[t3] = 1;
++used[t2];
next.insert(COMPOSE(k, kk));
}
for (int k = 0; k < row; ++k) {
int t2 = POS2(k, j), t3 = POS3(ct, t2);
if (used[t2] == 9 || used[t3])
continue;
next.erase(COMPOSE(k, j));
used[t3] = 1;
++used[t2];
next.insert(COMPOSE(k, j));
}
for (int k = row + nsqrt; k < n; ++k) {
int t2 = POS2(k, j), t3 = POS3(ct, t2);
if (used[t2] == 9 || used[t3])
continue;
next.erase(COMPOSE(k, j));
used[t3] = 1;
++used[t2];
next.insert(COMPOSE(k, j));
}
for (int k = 0; k < col; ++k) {
int t2 = POS2(i, k), t3 = POS3(ct, t2);
if (used[t2] == 9 || used[t3])
continue;
next.erase(COMPOSE(i, k));
used[t3] = 1;
++used[t2];
next.insert(COMPOSE(i, k));
}
for (int k = col + nsqrt; k < n; ++k) {
int t2 = POS2(i, k), t3 = POS3(ct, t2);
if (used[t2] == 9 || used[t3])
continue;
next.erase(COMPOSE(i, k));
used[t3] = 1;
++used[t2];
next.insert(COMPOSE(i, k));
}
goto NextLoop;
}
goto GoBack;
}
for (free(used); stack.size(); stack.pop_back())
free(stack.back().second);
}
};
总结
主要应用了深度搜索思想。