记录一下学习DLX的一些碎碎念吧。
其实dacing links 并不是一个算法,而是一个数据结构,这个算法的名字叫做x算法,其实,DLX本身也是回溯法的一种,它的优点在于可以非常快速地完成点的删除和复原。
这个数据结构是基于交叉十字链表实现的,用这个结构很自然,因为不旦要对行进行操作还要对列操作。
删除元素时,列元素的删除是因为不需要再考虑了,行元素的删除是因为不能考虑。
X算法使基于排除法的。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int INF = 0x7fffffff; const int NN = 350; const int MM = 750; int n,m; int cntc[NN]; int L[NN*MM],R[NN*MM],U[NN*MM],D[NN*MM]; int C[NN*MM]; int head; int mx[MM][NN]; int O[MM],idx; int ans[10][10]; void remove(int c) { int i,j; L[R[c]] = L[c]; R[L[c]] = R[c]; for(i = D[c]; i != c; i = D[i]) { for(j = R[i]; j != i; j = R[j]) { U[D[j]] = U[j]; D[U[j]] = D[j]; cntc[C[j]]--; } } } void resume(int c) { int i,j; R[L[c]] = c; L[R[c]] = c; for(i = D[c]; i != c; i = D[i]) { for(j = R[i]; j != i; j = R[j]) { U[D[j]] = j; D[U[j]] = j; cntc[C[j]]++; } } } bool dfs() { int i,j,c; if(R[head] == head) return true; int min = INF; for(i = R[head]; i != head; i = R[i]) { if(cntc[i] < min) { min = cntc[i]; c = i; } } remove(c); for(i = D[c]; i != c; i = D[i]) { O[idx++] = (i-1)/n; for(j = R[i]; j != i; j = R[j]) remove(C[j]); if(dfs()) return true; for(j = L[i]; j != i; j = L[j]) resume(C[j]); idx--; } resume(c); return false; } bool build() { int i,j,now,pre,first; idx = head = 0; for(i = 0; i < n; i++) { R[i] = i+1; L[i+1] = i; } R[n] = 0; L[0] = n; for(j = 1; j <= n; j++) { pre = j; cntc[j] = 0; for(i = 1; i <= m; i++) { if(mx[i][j]) { cntc[j]++; now = i*n+j; C[now] = j; D[pre] = now; U[now] = pre; pre = now; } } U[j] = pre; D[pre] = j; if(cntc[j] == 0) return false; } for(i = 1; i <= m; i++) { pre = first = -1; for(j = 1; j <= n; j++) { if(mx[i][j]) { now = i*n+j; if(pre == -1) first = now; else { R[pre] = now; L[now] = pre; } pre = now; } } if(first != -1) { R[pre] = first; L[first] = pre; } } return true; } int T; void print() { int i,j; int x,y,k; for(i = 0; i < idx; i++) { int r = O[i]; k = r%9; if(k==0) k = 9; int num = (r - k)/9 + 1; y = num%9; if(y == 0) y = 9; x = (num-y)/9 + 1; ans[x][y] = k; } if(idx == 0) printf("impossible\n"); else { for(i = 1; i <= 9; i++) { for(j = 1; j <= 9; j++) printf("%d",ans[i][j]); } cout<<endl; } } int map[MM][NN]; string str; void readdata() { char ch; int ind=0; for(int i = 1; i <= 9; i++) for(int j = 1; j <= 9; j++) { ch=str[ind++]; if(ch=='.') map[i][j]=0; else map[i][j]=ch-'0'; } } int main() { int i,j,k; int cases; char cao[12]; char s[12][12]; while(1) { cin>>str; if(str=="end")break; memset(mx,0,sizeof(mx)); readdata(); for(i = 1; i <= 9; i++) { for(j = 1; j <= 9; j++) { int t = (i-1)*9 + j; if(map[i][j] == 0) { for(k = 1; k <= 9; k++) { mx[9*(t-1)+k][t] = 1; mx[9*(t-1)+k][81+(i-1)*9+k] = 1; mx[9*(t-1)+k][162+(j-1)*9+k] = 1; mx[9*(t-1)+k][243+((i-1)/3*3+(j+2)/3-1)*9+k] = 1; } } else { k = map[i][j]; mx[9*(t-1)+k][t] = 1; mx[9*(t-1)+k][81+(i-1)*9+k] = 1; mx[9*(t-1)+k][162+(j-1)*9+k] = 1; mx[9*(t-1)+k][243+((i-1)/3*3+(j+2)/3-1)*9+k] = 1; } } } n = 324; m = 729; build(); dfs(); print(); } return 0; }
为了准备四省赛,感觉自己需要写一个做数独的板子出来,于是就又做了一遍,注释也加得想当详细
#include <iostream> #include <cstring> #include <algorithm> #include <cstdio> using namespace std; const int inf = 0x3f3f3f3f; const int M = 750; const int N = 400; struct DLX{ int D[M * N], U[M * N], L[M * N], R[M * N], col[M * N], row[M * N]; int H[M], S[N]; int m, n, id; int ansd; int ans[1010 * 4]; void init(int _m, int _n){ m = _m; n = _n; for(int i = 0; i <= n; i ++){ L[i] = i - 1; R[i] = i + 1; U[i] = D[i] = i; S[i] = 0; } id = n; L[0] = n; R[n] = 0; for(int i = 1; i <= m; i ++) H[i] = -1; } void link(int r, int c){ ++ S[col[++ id] = c]; row[id] = r; D[id] = D[c], U[D[c]] = id; D[c] = id, U[id] = c; if(H[r] == -1) H[r] = L[id] = R[id] = id; else{ R[id] = R[H[r]], L[R[H[r]]] = id; L[id] = H[r], R[H[r]] = id; } } 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]){ D[U[j]] = D[j]; U[D[j]] = U[j]; S[col[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]] = U[D[j]] = j; S[col[j]] ++; } } L[R[c]] = R[L[c]] = c; } bool dance(int d) { if(R[0] == 0){ ansd = d; return true; } int c = R[0]; //一个优化 找到列中包含1最多的列 因为这样有助于减少递归深度(很显然1多了 删掉的行也多 矩阵缩小得就快) for(int i = R[0]; i != 0; i = R[i]){ if(S[i] < S[c]) c = i; } remove(c); //搜索 for(int i = D[c]; i != c; i = D[i]){ ans[d] = row[i]; for(int j = R[i]; j != i; j = R[j]) remove(col[j]); if(dance(d + 1)) return true; for(int j = L[i]; j != i; j = L[j]) resume(col[j]); } resume(c); return false; } }dlx; char s[100]; int main(){ while(scanf("%s", s) != EOF){ if(strcmp(s, "end") == 0) break; dlx. init(81 * 9, 81 * 4); for(int i = 0; i < 9; i ++){ for(int j = 0; j < 9; j ++){ int x = i, y = j, z = (x / 3) * 3 + y / 3; //行,列,哪一宫 都是从0计数,因为k是从1开始的,所以可以完成从1~729的映射 int w = i * 9 + j; //对应于字符串中的位置,同样也是0~80这81个数的原始状态 if(s[w] == '.'){ for(int k = 1; k <= 9; k ++){ //对每一个i,j的组合都预留出9行,针对.或数字做不同变化 dlx. link(w * 9 + k, w + 1); dlx. link(w * 9 + k, 81 + (x * 9) + k); dlx. link(w * 9 + k, 162 + (y * 9) + k); dlx. link(w * 9 + k, 243 + (z * 9) + k); } } else{ int t = s[w] - '0'; dlx. link(w * 9 + t, w + 1); //81grid 每个小格只能放一个数字 dlx. link(w * 9 + t, 81 + x * 9 + t);//9row 每行数字k只能出现一次 dlx. link(w * 9 + t, 162 + y * 9 + t);//9col 每列数字k只能出现一次 dlx. link(w * 9 + t, 243 + z * 9 + t);//subgrid 每个3*3格子数字k只能出现一次 } } } dlx. dance(0); for(int i = 0; i < dlx. ansd; i ++){ int t = dlx.ans[i]; int a = (t - 1) / 9, b = (t - 1) % 9 + '1'; //除掉行中1~9这几个不同数的情况,剩下的就是行和列的组合。因为1~9是循环出现的,所以%9可以求出这一行代表着哪个数 s[(a / 9) * 9 + a % 9] = b; //a/9 转换成对应的行 a%9 转换成对应的列 } puts(s); } return 0; }