这是我做的第一个DLX解数独的题,建好模型就是解精确覆盖问题了,也不是很难,我不知道别人是怎么处理数独中原始的数据的,我的做法是先不管原有的的数建个729行*324列的十字双向链表,再处理掉原有的数字,再dance填空;
郁闷的是由于未知的错误我查了好几遍,处理原有数据的方法也变了好几变,查不出来后只好重写dance部分才行~等下再比比看看到底出问题在哪儿了。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int N=729; const int M=324; const int MM=5000; const int pl[9][9]={{0,0,0,1,1,1,2,2,2}, {0,0,0,1,1,1,2,2,2}, {0,0,0,1,1,1,2,2,2}, {3,3,3,4,4,4,5,5,5}, {3,3,3,4,4,4,5,5,5}, {3,3,3,4,4,4,5,5,5}, {6,6,6,7,7,7,8,8,8}, {6,6,6,7,7,7,8,8,8}, {6,6,6,7,7,7,8,8,8}}; int cnt,a[9][9]; int S[MM],V[MM],flag[MM],L[MM],R[MM],U[MM],D[MM],C[MM],H[MM]; void add(int i,int j) { cnt++; C[cnt]=j; H[cnt]=i; S[j]++; D[cnt]=j; U[cnt]=U[j]; if (V[i]) R[cnt]=V[i],L[cnt]=L[V[i]]; else R[cnt]=L[cnt]=cnt; V[i]=cnt; U[D[cnt]]=cnt; D[U[cnt]]=cnt; R[L[cnt]]=cnt; L[R[cnt]]=cnt; } void build_graph() { int i,j,k,x; cnt=M; for (i=0; i<=cnt; i++) { flag[i]=S[i]=0; R[i]=i+1; L[i+1]=i; C[i]=U[i]=D[i]=i; } L[0]=cnt; R[cnt]=0; for (i=0; i<=N; i++) V[i]=0; for (i=0; i<9; i++) for (j=0; j<9; j++) for (k=0; k<9; k++) { x=i*81+j*9+k; add(x,i*9+k+1); add(x,j*9+k+82); add(x,pl[i][j]*9+k+163); add(x,i*9+j+244); } } void remove(int c) { R[L[c]]=R[c]; L[R[c]]=L[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]) { D[U[j]]=j; U[D[j]]=j; S[C[j]]++; } L[R[c]]=R[L[c]]=c; } void pre_dance() { for (int i=0; i<9; i++) for (int j=0; j<9; j++) if (a[i][j]!=-1) { flag[i*9+a[i][j]+1]=1; flag[j*9+a[i][j]+82]=1; flag[pl[i][j]*9+a[i][j]+163]=1; flag[i*9+j+244]=1; } for (int i=1; i<=M; i++) if (flag[i]) remove(i); } bool dance() { if (!R[0]) return true; int s(N),c; for (int i=R[0]; i && s>1; i=R[i]) if (S[i]<s) s=S[i],c=i; remove(c); for (int i=D[c]; i!=c; i=D[i]) { a[H[i]/81][(H[i]/9)%9]=H[i]%9; for (int j=R[i]; j!=i; j=R[j]) remove(C[j]); if (dance()) return true; for (int j=L[i]; j!=i; j=L[j]) resume(C[j]); } resume(c); return false; } inline int str2digit(char *s) { if (s[0]=='?') return -1; else return s[0]-'1'; } int main() { int i,j,blank=0; char s[4]; while (scanf("%s",s)!=EOF) { if (blank) printf("\n"); else blank=1; a[0][0]=str2digit(s); for (j=1; j<9; j++) { scanf("%s",s); a[0][j]=str2digit(s); } for (i=1; i<9; i++) for (j=0; j<9; j++) { scanf("%s",s); a[i][j]=str2digit(s); } build_graph(); pre_dance(); dance(); for (i=0; i<9; i++) { for (j=0; j<8; j++) printf("%d ",a[i][j]+1); printf("%d\n",a[i][8]+1); } } return 0; }
偶又换了种预处理原有数据的方式,没有pre_dance了,直接在建图时解决,快一点了,而且改下K和KK值,以下代码能直接运用到16*16的数独(POJ3076)当中去,不过时间有点慢,有待优化~
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int N=729; const int M=324; const int K=9; const int KK=81; const int MM=5000; const int pl[K][K]={{0,0,0,1,1,1,2,2,2}, {0,0,0,1,1,1,2,2,2}, {0,0,0,1,1,1,2,2,2}, {3,3,3,4,4,4,5,5,5}, {3,3,3,4,4,4,5,5,5}, {3,3,3,4,4,4,5,5,5}, {6,6,6,7,7,7,8,8,8}, {6,6,6,7,7,7,8,8,8}, {6,6,6,7,7,7,8,8,8}}; int cnt,a[K][K]; int S[MM],V[MM],flag[MM],L[MM],R[MM],U[MM],D[MM],C[MM],H[MM]; inline void add(int i,int j) { if (flag[j]) return; cnt++; C[cnt]=j; H[cnt]=i; S[j]++; D[cnt]=j; U[cnt]=U[j]; if (V[i]) L[cnt]=V[i],R[cnt]=R[V[i]]; else L[cnt]=R[cnt]=cnt; V[i]=cnt; U[D[cnt]]=D[U[cnt]]=L[R[cnt]]=R[L[cnt]]=cnt; } inline void remove(const int &c) { R[L[c]]=R[c]; L[R[c]]=L[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]]--; } } inline void resume(const int &c) { for (int i=U[c]; i!=c; i=U[i]) for (int j=L[i]; j!=i; j=L[j]) { D[U[j]]=j; U[D[j]]=j; S[C[j]]++; } L[R[c]]=R[L[c]]=c; } inline void build_graph() { for (int i=0; i<=M; i++) { flag[i]=S[i]=0; U[i]=D[i]=i; R[i]=i+1; L[i+1]=i; } cnt=L[0]=M; R[M]=0; for (int i=0; i<=N; i++) V[i]=0; for (int i=0; i<K; i++) for (int j=0; j<K; j++) if (a[i][j]>=0) { flag[i*K+a[i][j] +1 ]=1; flag[j*K+a[i][j] +1+KK ]=1; flag[pl[i][j]*K+a[i][j]+1+2*KK]=1; flag[i*K +j+1+3*KK]=1; } int x,y1,y2,y3,y4; for (int i=0; i<K; i++) for (int j=0; j<K; j++) if (a[i][j]==-1) for (int k=0; k<K; k++) { x=i*KK+j*K+k; if (flag[y1=i*K+k+1]) continue; if (flag[y2=j*K+k+1+KK]) continue; if (flag[y3=pl[i][j]*K+k+1+2*KK]) continue; if (flag[y4=i*K+j+1+3*KK]) continue; add(x,y1); add(x,y2); add(x,y3); add(x,y4); } for (int i=1; i<=M; i++) if (flag[i]) remove(i); } bool dance() { if (!R[0]) return true; int s(N),c; for (int i=R[0]; i; i=R[i]) if (S[i]<s) s=S[c=i]; remove(c); for (int i=D[c]; i!=c; i=D[i]) { a[H[i]/KK][H[i]/K%K]=H[i]%K; for (int j=R[i]; j!=i; j=R[j]) remove(C[j]); if (dance()) return true; for (int j=L[i]; j!=i; j=L[j]) resume(C[j]); } resume(c); return false; } inline int str2digit(char *s) { if (s[0]=='?') return -1; else return s[0]-'1'; } int main() { int i,j,blank=0; char s[4]; while (scanf("%s",s)!=EOF) { if (blank) printf("\n"); else blank=1; a[0][0]=str2digit(s); for (j=1; j<9; j++) { scanf("%s",s); a[0][j]=str2digit(s); } for (i=1; i<9; i++) for (j=0; j<9; j++) { scanf("%s",s); a[i][j]=str2digit(s); } build_graph(); dance(); for (i=0; i<9; i++) { for (j=0; j<8; j++) printf("%d ",a[i][j]+1); printf("%d\n",a[i][8]+1); } } return 0; }找到影响效率的一个重要原因了!我把dance中的找c时的for (i=R[0]; i; i=R[i])改成for (i=L[0]; i; i=L[i])就让时间从2500ms+优化到290ms+(POJ3076)了,原因是每一格可填数的限制要强于每行每列每块的个数限制,而我的代码把每一格对应的列放在了后面,这一点还是去吃饭的路上想到的,看来及时吃饭很重要,在DLX算法中找列限制最强的找法是算法效率的一个关键点。