zoukankan      html  css  js  c++  java
  • 搜索: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("
    ");
    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
    ", puzzle[i]);
    154   }
    155   return 0;
    156 }
  • 相关阅读:
    RecycleView的万能适配器
    Android Fragment
    BottomNavigationBar底部导航条用法
    Bundle的用法
    登录页面(动态地与数据库匹配用户信息)
    LitePal用法详解
    BaseAdapter的优化
    Bmob使用心得
    字符串格式化
    元素NULL判断
  • 原文地址:https://www.cnblogs.com/aininot260/p/9629926.html
Copyright © 2011-2022 走看看