即求解数独游戏。算法
数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出必定的已知数字和解题条件,利用逻辑和推理,在其余的空格上填入1-9的数字。使1-9每一个数字在每一行、每一列和每一宫中都只出现一次,因此又称“九宫格”。1oop
如:上图中则有一个例子。spa
这题出如今算法类题目里我仍是很惊讶的,不过看到了,就顺手作了下。
利用惟一余数法,即用格位去找惟一可填数字,格位惟一可填数字称为惟余解(Naked Single)。若找不到惟余解,则随意选择一个数字进行深度搜索,并记录当前状态以便回溯。时间复杂度很高,应该是O(b^d)
。b
为可选解,d
为盘面的空位数。但因为存在惟余解,大多数状况下b = 1
,因此很快就能获得结果。code
#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); } };
主要应用了深度搜索思想。游戏