数独求解 DFS DLX
題目:Sudoku
題意:求解數(shù)獨。從樣例和結果來看應該是簡單難度的數(shù)獨
思路:DFS
設置3個數(shù)組,row[i][j] 判斷第i行是否放了j數(shù)字,col[i][j] 判斷第i列是否放了j數(shù)字。square[i/3][j/3][x]判斷第i/3行第j/3列個宮是否放置了x數(shù)字;
?
#include <iostream> #include <algorithm> #include <stdlib.h> #include <time.h> #include <cmath> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <queue> #include <stack> #include <set>#define c_false ios_base::sync_with_stdio(false); cin.tie(0) #define INF 0x3f3f3f3f #define INFL 0x3f3f3f3f3f3f3f3f #define zero_(x,y) memset(x , y , sizeof(x)) #define zero(x) memset(x , 0 , sizeof(x)) #define MAX(x) memset(x , 0x3f ,sizeof(x)) #define swa(x,y) {LL s;s=x;x=y;y=s;} using namespace std ; #define N 50005 const double PI = acos(-1.0); typedef long long LL ;bool col[10][10], row[10][10], square[5][5][10]; char mapp[10]; int MAP[10][10]; int n;bool dfs(int z){if(z>=81) return true;int x = z/9;int y = z%9;if(MAP[x][y])return dfs(z+1);for(int i = 1; i<= 9; i++){if(!row[x][i] && !col[y][i] && !square[x/3][y/3][i]){MAP[x][y] = i;row[x][i] = col[y][i] = square[x/3][y/3][i] = 1;if(dfs(z+1))return true;MAP[x][y] = 0;row[x][i] = col[y][i] = square[x/3][y/3][i] = 0;}}return false; } int main(){//freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);scanf("%d", &n);while(n--){//memset(MAP, 0, sizeof(MAP));memset(row, false, sizeof(row));memset(col, false, sizeof(col));memset(square, false, sizeof(square));for(int i = 0; i<9; i++){scanf("%s", mapp);for(int j = 0; j<9; j++){MAP[i][j] = mapp[j] - '0';int c = MAP[i][j];if(MAP[i][j]) {row[i][c] = col[j][c] = square[i/3][j/3][c] = 1;}}}dfs(0);for(int i=0;i<9;i++){for(int j=0;j<9;j++)printf("%d",MAP[i][j]);printf("\n");}}return 0; } View Code?
?題目:poj 3074
?思路:DLX是從數(shù)據(jù)結構角度優(yōu)化01矩陣的精確覆蓋和重復覆蓋的一種數(shù)據(jù)結構;
它用十字鏈表只存儲矩陣中的非0元,而01矩陣精確覆蓋dfs過程中矩陣會越來越稀疏而且每次恢復現(xiàn)場會浪費大量時間,DLX同時解決了這兩個問題。
本題關鍵在于將數(shù)獨問題轉化為01矩陣精確覆蓋。
精確覆蓋定義:
滿足以下條件的集合為一個精確覆蓋:
- S*中任意兩個集合沒有交集,即X中的元素在S*中出現(xiàn)最多一次
- S*中集合的全集為X,即X中的元素在S*中出現(xiàn)最少一次
合二為一,即X中的元素在S*中出現(xiàn)恰好一次。
舉例:
令??= {N,?O,?E,?P} 是集合X?= {1, 2, 3, 4}的一個子集的集合,并滿足:
- N?= { }
- O?= {1, 3}
- E?= {2, 4}
- P?= {2, 3}.
其中一個子集 {O,?E} 是?X的一個精確覆蓋,因為?O?= {1, 3} 而?E?= {2, 4} 的并集恰好是?X?= {1, 2, 3, 4}。同理, {N,?O,?E} 也是?X.的一個精確覆蓋。空集并不影響結論。
矩陣表示法:
包含關系可以用一個關系矩陣表示。. 矩陣每行表示S的一個子集,每列表示X中的一個元素。矩陣行列交點元素為1表示對應的元素在對應的集合中,不在則為0.
通過這種矩陣表示法,求一個精確覆蓋轉化為求矩陣的若干個行的集合,使每列有且僅有一個1。同時,該問題也是精確覆蓋的典型例題之一。
下圖為其中一個例子:
| ? | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| A | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
| B | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
| C | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
| D | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
| E | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
| F | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
我們可以發(fā)現(xiàn)B,D,F恰好保證每一列有且僅有一個1;
S* = {B,?D,?F} 便是一個精確覆蓋。
?DLX算法簡述:
如果讀取到一個格子是空的,那么加9行,分別表示這個格子填1到9這9個數(shù)字,如果讀取到的格子是一個數(shù)字,那么就加一行就可以了,然后列有9*9*4列,前81列表示這一行表示填的是第i行第j列的格子,接下來81列表示第i行填寫k,接下來81列表示第j列填寫k,最后81列表示對應九宮格填寫k。
步驟:
然后,就是正式的深搜了。在深搜的每一層:
1) 在十字鏈表表頭中找出元素最少且非零的一列,將該列以及列中包含的所有行元素從十字鏈表中移除;
2) 枚舉列中的每行(作為解的一部分),將與該行相交的所有其他列以及這些列包含的行元素在十字鏈表中移除,遞歸下一層深搜;
3) 遞歸返回時,需要將2)中移除的相應列和行添加回十字鏈表;
4) 所有行枚舉完畢時,需要將1)中移除的相應列和行添加回十字鏈表。
注意,對鏈表進行增刪操作時,需要以相反的順序修改各結點。同時,也需要動態(tài)維護表頭的元素數(shù)。
?
#include<iostream> #include<cstring> #include<string> #include<cstdio> #include<algorithm> #include<vector> #include<algorithm>using namespace std; // 列:(行+列+塊)*9種可能+9*9個格子 // 行: 9*9*9 表示第i行第j列填k const int MAXN=(9+9+9)*9+9*9+9*9*9*9*9*4+10; #define INF 0xFFFFFF int size; int head,sz; int U[MAXN],D[MAXN],L[MAXN],R[MAXN]; int H[MAXN],ROW[MAXN],C[MAXN],S[MAXN],O[MAXN];void remove(int c) {L[R[c]]=L[c];R[L[c]]=R[c];for(int i=D[c];i!=c;i=D[i]){for(int j=R[i];j!=i;j=R[j]){U[D[j]]=U[j];D[U[j]]=D[j];--S[C[j]];}} }void resume(int c) {for(int i=U[c];i!=c;i=U[i]){for(int j=L[i];j!=i;j=L[j]){++S[C[j]];U[D[j]]=j;D[U[j]]=j;}}L[R[c]]=c;R[L[c]]=c; }bool dfs(int k) {if(R[head]==head){sort(O,O+9*9);int p=0;for(int i=0;i<9;i++){for(int j=0;j<9;j++){int num=O[p++];//cout<<num<<endl;num=num-(i*9+j)*9;printf("%d",num);}}printf("\n");return true;}int s=INF,c;for (int t=R[head];t!=head;t=R[t]){if (S[t]<s){s=S[t];c=t;}}remove(c);for(int i=D[c];i!=c;i=D[i]){O[k]=ROW[i];for(int j=R[i];j!=i;j=R[j])remove(C[j]);if(dfs(k+1))return true;for(int j=L[i];j!=i;j=L[j])resume(C[j]);}resume(c);return false; }void initDL(int n) {head=0;for(int i=0;i<=n;i++){U[i]=i;D[i]=i;L[i]=i-1;R[i]=i+1;S[i]=0;}R[n]=0;L[0]=n;S[0]=INF+1;sz=n+1;memset(H,0,sizeof(H)); }void insert(int i, int j) {if(H[i]){L[sz]=L[H[i]];R[sz]=H[i];L[R[sz]]=sz;R[L[sz]]=sz;}else{L[sz]=sz;R[sz]=sz;H[i]=sz;}U[sz]=U[j];D[sz]=j;U[D[sz]]=sz;D[U[sz]]=sz;C[sz]=j;ROW[sz]=i;++S[j];++sz; }char str[200];void build() {int p=0;initDL(9*9*4);for(int i=0;i<9;i++)for(int j=1;j<=9;j++,p++){int base=(i*9+j-1)*9;if(str[p]=='.'){for(int k=1;k<=9;k++){int r;r=base+k;//第i行有數(shù)字kinsert(r,i*9+k);//第j列有數(shù)字kinsert(r,9*9+(j-1)*9+k);//第k塊有數(shù)字kint block=(j-1)/3*3+i/3;insert(r,9*9*2+block*9+k);//第i行j列有一個數(shù)字(限制一個格子只填一個數(shù))insert(r,9*9*3+i*9+j);}}else{int k=str[p]-'0';int r=base+k;//第i行有數(shù)字kinsert(r,i*9+k);//第j列有數(shù)字kinsert(r,9*9+(j-1)*9+k);//第k塊有數(shù)字kint block=(j-1)/3*3+i/3;insert(r,9*9*2+block*9+k);//第i行j列有一個數(shù)字(限制一個格子只填一個數(shù))insert(r,9*9*3+i*9+j);}} }int main() {size=9; //9*9數(shù)獨while(~scanf("%s",str)){if(strcmp(str,"end")==0)break;build();dfs(0);}return 0; } View Code?
轉載于:https://www.cnblogs.com/yoyo-sincerely/p/5309522.html
總結
以上是生活随笔為你收集整理的数独求解 DFS DLX的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kali中wireshark打开后错误
- 下一篇: 将excel文件中的数据导入到mysql