搜索: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 }

 

posted @ 2018-09-11 20:04  静听风吟。  阅读(1683)  评论(0编辑  收藏  举报