搜索:DLX算法
精确覆盖问题:在一个0-1矩阵中,选定部分行,使得每一列都有且只有一个1。求解一种选法
舞蹈链(Dance Link),也就是一个循环十字链表,可以快速的删掉和恢复某行某列
结合了舞蹈链的搜索就称作DLX算法
这里贴一个用DLX算法解决16×16数独的代码
9×9的直接暴力会更好
1 // LA2659 Sudoku 2 // Rujia Liu 3 #include<cstdio> 4 #include<cstring> 5 #include<vector> 6 7 using namespace std; 8 9 const int maxr = 5000; 10 const int maxn = 2000; 11 const int maxnode = 20000; 12 13 // 行编号从1开始,列编号为1~n,结点0是表头结点; 结点1~n是各列顶部的虚拟结点 14 struct DLX { 15 int n, sz; // 列数,结点总数 16 int S[maxn]; // 各列结点数 17 18 int row[maxnode], col[maxnode]; // 各结点行列编号 19 int L[maxnode], R[maxnode], U[maxnode], D[maxnode]; // 十字链表 20 21 int ansd, ans[maxr]; // 解 22 23 void init(int n) { // n是列数 24 this->n = n; 25 26 // 虚拟结点 27 for(int i = 0 ; i <= n; i++) { 28 U[i] = i; D[i] = i; L[i] = i-1, R[i] = i+1; 29 } 30 R[n] = 0; L[0] = n; 31 32 sz = n + 1; 33 memset(S, 0, sizeof(S)); 34 } 35 36 void addRow(int r, vector<int> columns) { 37 int first = sz; 38 for(int i = 0; i < columns.size(); i++) { 39 int c = columns[i]; 40 L[sz] = sz - 1; R[sz] = sz + 1; D[sz] = c; U[sz] = U[c]; 41 D[U[c]] = sz; U[c] = sz; 42 row[sz] = r; col[sz] = c; 43 S[c]++; sz++; 44 } 45 R[sz - 1] = first; L[first] = sz - 1; 46 } 47 48 // 顺着链表A,遍历除s外的其他元素 49 #define FOR(i,A,s) for(int i = A[s]; i != s; i = A[i]) 50 51 void remove(int c) { 52 L[R[c]] = L[c]; 53 R[L[c]] = R[c]; 54 FOR(i,D,c) 55 FOR(j,R,i) { U[D[j]] = U[j]; D[U[j]] = D[j]; --S[col[j]]; } 56 } 57 58 void restore(int c) { 59 FOR(i,U,c) 60 FOR(j,L,i) { ++S[col[j]]; U[D[j]] = j; D[U[j]] = j; } 61 L[R[c]] = c; 62 R[L[c]] = c; 63 } 64 65 // d为递归深度 66 bool dfs(int d) { 67 if (R[0] == 0) { // 找到解 68 ansd = d; // 记录解的长度 69 return true; 70 } 71 72 // 找S最小的列c 73 int c = R[0]; // 第一个未删除的列 74 FOR(i,R,0) if(S[i] < S[c]) c = i; 75 76 remove(c); // 删除第c列 77 FOR(i,D,c) { // 用结点i所在行覆盖第c列 78 ans[d] = row[i]; 79 FOR(j,R,i) remove(col[j]); // 删除结点i所在行能覆盖的所有其他列 80 if(dfs(d+1)) return true; 81 FOR(j,L,i) restore(col[j]); // 恢复结点i所在行能覆盖的所有其他列 82 } 83 restore(c); // 恢复第c列 84 85 return false; 86 } 87 88 bool solve(vector<int>& v) { 89 v.clear(); 90 if(!dfs(0)) return false; 91 for(int i = 0; i < ansd; i++) v.push_back(ans[i]); 92 return true; 93 } 94 95 }; 96 97 ////////////// 题目相关 98 #include<cassert> 99 100 DLX solver; 101 102 const int SLOT = 0; 103 const int ROW = 1; 104 const int COL = 2; 105 const int SUB = 3; 106 107 // 行/列的统一编解码函数。从1开始编号 108 int encode(int a, int b, int c) { 109 return a*256+b*16+c+1; 110 } 111 112 void decode(int code, int& a, int& b, int& c) { 113 code--; 114 c = code%16; code /= 16; 115 b = code%16; code /= 16; 116 a = code; 117 } 118 119 char puzzle[16][20]; 120 121 bool read() { 122 for(int i = 0; i < 16; i++) 123 if(scanf("%s", puzzle[i]) != 1) return false; 124 return true; 125 } 126 127 int main() { 128 int kase = 0; 129 while(read()) { 130 if(++kase != 1) printf("\n"); 131 solver.init(1024); 132 for(int r = 0; r < 16; r++) 133 for(int c = 0; c < 16; c++) 134 for(int v = 0; v < 16; v++) 135 if(puzzle[r][c] == '-' || puzzle[r][c] == 'A'+v) { 136 vector<int> columns; 137 columns.push_back(encode(SLOT, r, c)); 138 columns.push_back(encode(ROW, r, v)); 139 columns.push_back(encode(COL, c, v)); 140 columns.push_back(encode(SUB, (r/4)*4+c/4, v)); 141 solver.addRow(encode(r, c, v), columns); 142 } 143 144 vector<int> ans; 145 assert(solver.solve(ans)); 146 147 for(int i = 0; i < ans.size(); i++) { 148 int r, c, v; 149 decode(ans[i], r, c, v); 150 puzzle[r][c] = 'A'+v; 151 } 152 for(int i = 0; i < 16; i++) 153 printf("%s\n", puzzle[i]); 154 } 155 return 0; 156 }