精确覆盖问题:在一个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 }
来源:oschina
链接:https://my.oschina.net/u/4313009/blog/3829653