回溯搜尋(數獨)
阿新 • • 發佈:2020-09-23
這是我以前寫的,現在轉存在部落格上
玩家需要根據9×9盤面上的已知數字,推理出所有剩餘空格的數字,並滿足每一行、每一列、每一個粗線宮(3×3)內的數字均含1-9,不重複。
給出格子的行和列我們可以確定格子在哪一個宮
void cell(int x,int y)
{
return x / 3 * 3 + y / 3;
}
我們只需要列舉其餘所有的點
for(int map[][]=1;map[][]<10;map[][]++){ if(check()){ //每一層的check()是為了減少搜尋次數的 for(int map[][]=1;map[][]<10;map[][]++){ if(check()){ for(int map[][]=1;map[][]<10;map[][]++){ if(check()){ .... //可以用遞迴實現 .... // } } } } } }
這就是DFS,深度優先搜尋
遞迴寫法模板
//回溯法 void dfs(答案,搜尋層數,其他引數){ if(層數==maxdeep){ 更新答案; return; } (剪枝) for(列舉下一層可能的狀態){ if(check()){ //更新全域性變量表示狀態的變數; //dfs(答案+新狀態增加的價值,層數+1,其他引數); //新狀態必須是確定的,不能改變。 //還原全域性變量表示狀態的變數; } } }
構造標記陣列
markrow[row][number]; //記錄某一列是否存在某一個數字
markcol[col][number]; //記錄某一行是否存在某一個數字
markbox[cell][number]; //記錄某一宮是否存在某一個數字
這裡需要注意深度搜索的順序會很大程度的影響到演算法的效率
程式碼
#include<iostream> #include<algorithm> #include<utility> #include<cstdio> #include<cstring> using namespace std; int map[9][9]; pair<int, int> Next[81]; int check = 0; // 已知的數的個數 int k = 0; //遞迴的深度 int markrank[9][10]; //記錄某一列是否存在某一個數字 int markrow[9][10]; //記錄某一列是否存在某一個數字 int markcell[9][10]; //記錄某一宮是否存在某一個數字 int cell(int x, int y) { return x / 3 * 3 + y / 3; } // count函式用於記錄(x,y)格子的還有幾種可能情況 int count(int x, int y) { int a[9] = { 0 }; int sum = 0; for (int i = 1; i <= 9; i++) { if (markrank[x][i] == 1 || markrow[y][i] == 1 || markcell[cell(x, y)][i] == 1) a[i - 1] = 1; } for (int i = 0; i < 9; i++) { if (a[i] == 1) sum++; } return sum; } //next()函式的作用是求出當前可能情況最少的格子 pair<int, int> next() { pair<int, int> P2; int f = 0; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (map[i][j] == 0 && f == 1) { if (count(P2.first, P2.second) < count(i, j)) { P2.first = i; P2.second = j; } } else if (map[i][j] == 0 && f == 0) { P2.first = i; P2.second = j; f++; } } } return P2; } void dfs(int x, int y) { if (k == 81 - check) { for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { cout << map[i][j] << " "; } cout << endl; } } else { for (int i = 1; i < 10; i++) { if (markrank[x][i]==0 && markrow[y][i]==0 && markcell[cell(x,y)][i]==0){ map[x][y] = i; markcell[cell(x, y)][i] = 1; markrank[x][i] = 1; markrow[y][i] = 1; k++; if (Next[k].first == -1) { pair<int, int> P = next(); Next[k] = P; } dfs(Next[k].first, Next[k].second); k--; markcell[cell(x, y)][i] = 0; markrank[x][i] = 0; markrow[y][i] = 0; } } } } int main() { memset(markrank, 0, sizeof(markrank)); memset(markrow, 0, sizeof(markrow)); memset(markcell, 0, sizeof(markcell)); memset(Next, -1, sizeof(Next)); for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { scanf("%d", &map[i][j]); if (map[i][j] != 0) check++; markcell[cell(i, j)][map[i][j]] = 1; markrank[i][map[i][j]] = 1; markrow[j][map[i][j]] = 1; } } pair<int, int> P = next(); dfs(P.first, P.second); return 0; }
解決數獨的其他演算法:dancing links 演算法
號稱世界上最難的數獨
0 0 5 3 0 0 0 0 0
8 0 0 0 0 0 0 2 0
0 7 0 0 1 0 5 0 0
4 0 0 0 0 5 3 0 0
0 1 0 0 7 0 0 0 6
0 0 3 2 0 0 0 8 0
0 6 0 5 0 0 0 0 9
0 0 4 0 0 0 0 3 0
0 0 0 0 0 9 7 0 0