即求解數獨遊戲。算法
數獨盤面是個九宮,每一宮又分爲九個小格。在這八十一格中給出必定的已知數字和解題條件,利用邏輯和推理,在其餘的空格上填入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); } };
主要應用了深度搜索思想。遊戲