Dancing_Links总结 【by AbandonZHANG】

参考文章:

1. 《Dancing Links》 by Donald E.Knuth (Knuth老人家以这篇论文公布和诠释了他发明的Dancing_Links)

附一个中文版的地址:http://sqybi.com/works/dlxcn/(仅供英语不好的人看。。。有能力的还是看原文比较好~~~)

2. 《Dancing Links在搜索中的应用》 by momodi (里面有 momodi 大牛的DLX模板)

Dancing Links

Dancing Links实质上就是一个能够“优美地”进行恢复删除操作的双向链表。Knuth更多的把它定义为一种技巧而不是新的数据结构。

这种技巧便是用  (1)L[R[x]]=L[x],  R[L[x]]=R[x]  进行删除结点操作

和用       (2)L[R[x]]=x,  R[L[x]]=x     进行恢复结点操作。

整个技巧的亮点在于(2)可以如此“优美地”恢复已经删除的结点。当用这种技巧不断进行删除、恢复操作时,链表的指针就像在跳舞一样,所以Knuth愿意叫它Dancing Links。

那么随之而来就是另一个重要的问题:这个优美的东西能够干什么。

Dancing Links最重要的一个特征就是它可以高效的不断删除、恢复结点,尤其是恢复。那么哪里会用到恢复结点呢?最显然的当然是回溯,即深度优先搜索(DFS)。所以Dancing Links一个重要的应用当然就是优化DFS。

精确覆盖问题------DLX (from  Wiki )

 

具体到一个模型就是

给定一个由0 和1 组成的矩阵,是否能找到一个行的集合,使得

集合中每一列都恰好包含一个1?例如,下面这个矩阵

list 3(3)

就包含了这样一个集合(第1,4,5行)。我们把列想象成全集的一些元素,而行看作全集的一些子集;或者我们可以把行想象成全集的一些元素,而把列看作全集的一些子集;那么这个问题就是要求寻找一批元素,它们与每个子集恰好有一个交点。不管怎么说,这都是一个很难的问题,众所周知,当每行恰包含3个1时,这是个一个NP-完全问题。自然,作为首选的算法就是回溯了。

 

 

解决精确覆盖问题(Algorithm X ---> DLX)

(from 《Dancing Links》D.Knuth ---CN (论文已经讲的很清楚了~~~)

  对于接下来的非确定性算法,Knuth将称之为X算法,它能够找到由特定的01矩阵A定义的精确覆盖问题的所有解。

如果A是空的,问题解决;成功终止。
否则,选择一个列c(确定的)。
选择一个行r,满足 A[r, c]=1 (不确定的)。
把r包含进部分解。
对于所有满足 A[r,j]=1 的j,
  从矩阵A中删除第j列;
  对于所有满足 A[i,j]=1 的i,
    从矩阵A中删除第i行。
在不断减少的矩阵A上递归地重复上述算法。

DLX C++模板:

DLX 模板
  1 DLX 模板
  2 
  3 //Dancing Links
  4 const int N = 350;
  5 const int M = 20 ;
  6 int R[M*N],L[M*N],U[M*N],D[M*N],O[M*N],S[M*N],C[M*N];
  7 int h;      //head
  8 int n,m;
  9 
 10 void remove(int &c)
 11 {
 12     L[R[c]]=L[c];
 13     R[L[c]]=R[c];
 14     for (int i=D[c];i!=c;i=D[i])
 15     //remove i that A[i,c]==1
 16         for (int j=R[i];j!=i;j=R[j])
 17         {
 18             //remove j that A[i,j]==1
 19             U[D[j]]=U[j];
 20             D[U[j]]=D[j];
 21             --S[C[j]];
 22             //decrese the count of column C[j];
 23         }
 24 }
 25 
 26 void resume(int &c)
 27 {
 28     for (int i=U[c];i!=c;i=U[i])
 29         for (int j=L[i];j!=i;j=L[j])
 30         {
 31             U[D[j]]=j;
 32             D[U[j]]=j;
 33             ++S[C[j]];
 34         }
 35 
 36     L[R[c]]=c;
 37     R[L[c]]=c;
 38 }
 39 bool dfs(int k)
 40 {
 41     if (R[h] == h)
 42     {
 43         //one of the answers has been found.
 44         return true;
 45     }
 46 
 47     int c,s=INT_MAX;
 48     for (int i=R[h];i!=h;i=R[i])
 49         if (S[i]<s)
 50         {
 51             s=S[i];
 52             c=i;
 53         }
 54 
 55     remove(c);
 56     for (int i=D[c];i!=c;i=D[i])
 57     {
 58         O[k]=i;
 59         for (int j=R[i];j!=i;j=R[j])
 60             remove(C[j]);
 61         if (dfs(k+1))
 62             return true;
 63         for (int j=L[i];j!=i;j=L[j])
 64             resume(C[j]);
 65     }
 66     resume(c);
 67     return false;
 68 
 69 }
 70 
 71 
 72 //根据题目自己写初始化构造链表函数
 73 void Initialize_DancingLinks()
 74 {
 75     h=0;
 76 
 77     //initialize the column head list.
 78     L[0]=m;
 79     for (int j=1;j<=m;j++)
 80     {
 81         R[j-1]=j;
 82         L[j]=j-1;
 83         S[j]=0;
 84     }
 85     R[m]=0;
 86 
 87     //initialize Dancing-Links
 88     for (int j=1;j<=m;j++)
 89     {
 90         for (int i=1;i<=n;i++)
 91         {
 92             U[i*m+j]=(i-1)*m+j;
 93             D[(i-1)*m+j]=i*m+j;
 94             C[i*m+j]=j;
 95             S[j]++;
 96         }
 97         D[n*m+j]=j;
 98         U[j]=n*m+j;
 99     }
100 
101     for (int i=1;i<=n;i++)
102     {
103         for (int j=2;j<=m;j++)
104         {
105             R[i*m+j-1]=i*m+j;
106             L[i*m+j]=i*m+j-1;
107         }
108         R[i*m+m]=i*m+1;
109         L[i*m+1]=i*m+m;
110     }
111 
112     int d[20][305];
113     for (int i=1;i<=n;i++)
114         for (int j=1;j<=m;j++)
115             scanf("%d",&d[i][j]);
116 
117     for (int i=1;i<=n;i++)
118         for (int j=1;j<=m;j++)
119             if (d[i][j]==0)
120             {
121                 D[U[i*m+j]]=D[i*m+j];
122                 U[D[i*m+j]]=U[i*m+j];
123                 S[C[i*m+j]]--;
124                 R[L[i*m+j]]=R[i*m+j];
125                 L[R[i*m+j]]=L[i*m+j];
126             }
127 }
128 
129 //在main()里调用dfs(0)执行程序

这里说一下根据矩阵怎么建立链表:(不推荐此法了……)(对照模板里面Initialize_DancingLinks()函数看)

1.首先不管矩阵是0还是1都加入到链表中。因为这样好给新建立的链表编号--->假如有n行m列矩阵,1-m号表示1-m列表头。那么第i行j列的结点号即为i*m+j。

2.遍历链表(表已经建立了哪行哪列下标号也很清楚不难吧~~~),如果当前位置为0,则删除该结点。(L[R[x]]=L[x],  R[L[x]]=R[x],  U[D[x]]=U[x],  D[U[x]]=D[x])

(这种方法很偷懒吧?~^_^……)

 

PS:这种建表方式也浪费了大量的时间。矩阵小点儿还好,解决数独就不行了。(POJ 3074用这种建法TLE到死。。。标准建法200MS。。。)

所以建立链表的方法还是以下面POJ 3074的代码中的方法(即数独模板中的方法)为准。

 

习题:

 

POJ 3740  (DLX入门题,就是上面那道01矩阵精确覆盖的题)

http://poj.org/problem?id=3740

POJ 3740
  1 POJ 3740
  2 
  3 #include <iostream>
  4 #include <cstdio>
  5 #include <cstdlib>
  6 #include <cmath>
  7 #include <iomanip>
  8 #include <climits>
  9 #include <vector>
 10 #include <stack>
 11 #include <queue>
 12 #include <set>
 13 #include <map>
 14 #include <algorithm>
 15 #include <string>
 16 #include <cstring>
 17 
 18 using namespace std;
 19 
 20 typedef long long LL;
 21 const double EPS = 1e-11;
 22 
 23 //Dancing Links
 24 const int N = 350;
 25 const int M = 20 ;
 26 int R[M*N],L[M*N],U[M*N],D[M*N],O[M*N],S[M*N],C[M*N];
 27 int h;      //head
 28 int n,m;
 29 
 30 void remove(int &c)
 31 {
 32     L[R[c]]=L[c];
 33     R[L[c]]=R[c];
 34     for (int i=D[c];i!=c;i=D[i])
 35     //remove i that A[i,c]==1
 36         for (int j=R[i];j!=i;j=R[j])
 37         {
 38             //remove j that A[i,j]==1
 39             U[D[j]]=U[j];
 40             D[U[j]]=D[j];
 41             --S[C[j]];
 42             //decrese the count of column C[j];
 43         }
 44 }
 45 
 46 void resume(int &c)
 47 {
 48     for (int i=U[c];i!=c;i=U[i])
 49         for (int j=L[i];j!=i;j=L[j])
 50         {
 51             U[D[j]]=j;
 52             D[U[j]]=j;
 53             ++S[C[j]];
 54         }
 55 
 56     L[R[c]]=c;
 57     R[L[c]]=c;
 58 }
 59 bool dfs(int k)
 60 {
 61     if (R[h] == h)
 62     {
 63         //one of the answers has been found.
 64         return true;
 65     }
 66 
 67     int c,s=INT_MAX;
 68     for (int i=R[h];i!=h;i=R[i])
 69         if (S[i]<s)
 70         {
 71             s=S[i];
 72             c=i;
 73         }
 74 
 75     remove(c);
 76     for (int i=D[c];i!=c;i=D[i])
 77     {
 78         O[k]=i;
 79         for (int j=R[i];j!=i;j=R[j])
 80             remove(C[j]);
 81         if (dfs(k+1))
 82             return true;
 83         for (int j=L[i];j!=i;j=L[j])
 84             resume(C[j]);
 85     }
 86     resume(c);
 87     return false;
 88 
 89 }
 90 
 91 void Initialize_DancingLinks()
 92 {
 93     h=0;
 94 
 95     //initialize the column head list.
 96     L[0]=m;
 97     for (int j=1;j<=m;j++)
 98     {
 99         R[j-1]=j;
100         L[j]=j-1;
101         S[j]=0;
102     }
103     R[m]=0;
104 
105     //initialize Dancing-Links
106     for (int j=1;j<=m;j++)
107     {
108         for (int i=1;i<=n;i++)
109         {
110             U[i*m+j]=(i-1)*m+j;
111             D[(i-1)*m+j]=i*m+j;
112             C[i*m+j]=j;
113             S[j]++;
114         }
115         D[n*m+j]=j;
116         U[j]=n*m+j;
117     }
118 
119     for (int i=1;i<=n;i++)
120     {
121         for (int j=2;j<=m;j++)
122         {
123             R[i*m+j-1]=i*m+j;
124             L[i*m+j]=i*m+j-1;
125         }
126         R[i*m+m]=i*m+1;
127         L[i*m+1]=i*m+m;
128     }
129 
130     int d[20][305];
131     for (int i=1;i<=n;i++)
132         for (int j=1;j<=m;j++)
133             scanf("%d",&d[i][j]);
134 
135     for (int i=1;i<=n;i++)
136         for (int j=1;j<=m;j++)
137             if (d[i][j]==0)
138             {
139                 D[U[i*m+j]]=D[i*m+j];
140                 U[D[i*m+j]]=U[i*m+j];
141                 S[C[i*m+j]]--;
142                 R[L[i*m+j]]=R[i*m+j];
143                 L[R[i*m+j]]=L[i*m+j];
144             }
145 }
146 
147 int main()
148 {
149     while(scanf("%d%d",&n,&m)!=EOF)
150     {
151         memset(C,0,sizeof(C));
152         Initialize_DancingLinks();
153         if (dfs(0))
154             printf("Yes, I found it\n");
155         else
156             printf("It is impossible\n");
157     }
158     return 0;
159 }
数独(Sudoku)转化精确覆盖问题思路:

  转化精确覆盖问题的思路一般是:  有多少种选择就建立多少行;有多少个限制就建立多少列 

  比如数独问题转化思路就是:(N阶数独)设置N*N*N行。每一行的状态表示数独第i行第j列数为k。设置(N+N+N)*N+N*N(即4*N*N)列。前面(N+N+N)*N列状态分别表示第i行有数k、第j列有数k、第p个九宫格有数k。后面N*N列限制数独的每个格子只能填一个数。如果没有后面N*N列限制,则搜索时一个格子可能被填2个数。

  然后就是注意最后答案的转化(具体看模板)

 

POJ 3074   (数独入门题,标准9*9数独 ------Sudoku模板~~~)

http://poj.org/problem?id=3074

POJ 3074
  1 POJ 3074
  2  #include <iostream>
  3  #include <cstdio>
  4  #include <cstdlib>
  5  #include <cmath>
  6  #include <iomanip>
  7  #include <climits>
  8  #include <vector>
  9  #include <stack>
 10  #include <queue>
 11  #include <set>
 12  #include <map>
 13  #include <algorithm>
 14  #include <string>
 15  #include <cstring>
 16  
 17  using namespace std;
 18  
 19  typedef long long LL;
 20  const double EPS = 1e-11;
 21  
 22  //Dancing Links
 23  //列(N+N+N)*N+N*N=4*N*N.
 24  #define N 750   //>9*9*9
 25  #define M 350   //>4*9*9
 26  const int P=9;  //p阶数独
 27  int h;
 28  int L[N*M],R[N*M],U[N*M],D[N*M],S[N*M],C[N*M],O[N*M];
 29  
 30  int n,m;
 31  void remove (const int &c)
 32  {
 33      L[R[c]]=L[c];
 34      R[L[c]]=R[c];
 35      for (int i=D[c];i!=c;i=D[i])
 36          for (int j=R[i];j!=i;j=R[j])
 37          {
 38              U[D[j]]=U[j];
 39              D[U[j]]=D[j];
 40              --S[C[j]];
 41          }
 42  }
 43  
 44  void resume (const int &c)
 45  {
 46      for (int i=U[c];i!=c;i=U[i])
 47          for (int j=L[i];j!=i;j=L[j])
 48          {
 49              U[D[j]]=j;
 50              D[U[j]]=j;
 51              ++S[C[j]];
 52          }
 53      L[R[c]]=c;
 54      R[L[c]]=c;
 55  }
 56  
 57  bool Dance(int k)
 58  {
 59      if (R[h]==h)
 60      {
 61          sort(O,O+k);
 62          for (int i=0;i<k;i++)
 63              printf("%d",(O[i]-1)/m%P==0?P:((O[i]-1)/m%P));
 64          printf("\n");
 65  
 66          return true;
 67      }
 68  
 69      int c,ss=INT_MAX;
 70  
 71      for (int i=R[h];i!=h;i=R[i])
 72          if (S[i]<ss)
 73          {
 74              ss=S[i];
 75              c=i;
 76          }
 77  
 78      remove(c);
 79      for (int i=D[c];i!=c;i=D[i])
 80      {
 81          O[k]=i;
 82          for (int j=R[i];j!=i;j=R[j])
 83              remove(C[j]);
 84          if (Dance(k+1))
 85              return true;
 86          for (int j=L[i];j!=i;j=L[j])
 87              resume(C[j]);
 88      }
 89      resume(c);
 90  
 91      return false;
 92  }
 93  
 94  //Initialize
 95  void Link(char s[])
 96  {
 97      int d[N][M];
 98      memset(d,0,sizeof(d));
 99      memset(O,0,sizeof(O));
100  
101      //preprocess the sudoku 数独转换精确覆盖问题矩阵形式
102      int q=0;
103      for (int i=1;i<=P;i++)
104          for (int j=1;j<=P;j++)
105          {
106              if (s[q]=='.')
107              {
108                  for (int k=1;k<=P;k++)
109                  {
110                      int rr=((i-1)*P+j-1)*P+k;                   //行。(9*9*9)行 表示数独第i行第j列数填k
111                      d[rr][(i-1)*P+k]=1;                         //列。前(9*9)列 表示数独第i行有数k
112                      d[rr][(j-1)*P+k+P*P]=1;                     //列。(9*9)列 表示数独第j行有数k
113                      d[rr][((i-1)/3*3+(j-1)/3)*P+k+2*P*P]=1;     //列。(9*9)列 表示数独第p个九宫格有数k
114                      d[rr][(i-1)*P+j+3*P*P]=1;                   //列。(9*9)列 表示数独第i行第j行有一个数(防止一个格子填多个数)
115                  }
116              }
117              else
118              {
119                  int num=s[q]-'0';
120                  int rr=((i-1)*P+j-1)*P+num;                 //行。(9*9*9)行 表示数独第i行第j列数填k
121                  d[rr][(i-1)*P+num]=1;                       //列。前(9*9)列 表示数独第i行有数k
122                  d[rr][(j-1)*P+num+P*P]=1;                   //列。(9*9)列 表示数独第j行有数k
123                  d[rr][((i-1)/3*3+(j-1)/3)*P+num+2*P*P]=1;   //列。(9*9)列 表示数独第p个九宫格有数k
124                  d[rr][(i-1)*P+j+3*P*P]=1;                   //列。(9*9)列 表示数独第i行第j行有一个数(防止一个格子填多个数)
125              }
126  
127              q++;
128          }
129  
130      //Initialize the all matrix to list.
131      int x[N],row[N],col[M];           //x表示当前行中的第一个链,row表示当前行中上一个插入的链、col表示当前列中上一个插入的链。
132      h=0;
133      for (int i=1;i<=m;i++)
134      {
135          R[i-1]=i;
136          L[i]=i-1;
137          S[i]=0;
138          col[i]=i;
139      }
140      col[0]=0;
141      L[h]=m;
142      R[m]=h;
143  
144      for (int i=1;i<=n;i++)
145      {
146          x[i]=0;                             //行第一个链表
147          for (int j=1;j<=m;j++)
148              if (d[i][j])
149              {
150                  int index=i*(4*P*P)+j;      //带插入的列表下标。
151                  if (!x[i])
152                  {
153                      row[i]=x[i]=index;
154                  }
155                  else
156                  {
157                      R[row[i]]=index;
158                      L[index]=row[i];
159                  }
160                  D[col[j]]=index;
161                  U[index]=col[j];
162                  row[i]=col[j]=index;
163                  C[index]=j;
164                  S[j]++;
165              }
166      }
167  
168      for (int i=1;i<=n;i++)
169          if (x[i])
170          {
171              L[x[i]]=row[i];
172              R[row[i]]=x[i];
173          }
174      for (int j=1;j<=m;j++)
175      {
176          D[col[j]]=j;
177          U[j]=col[j];
178      }
179  }
180  
181  int main()
182  {
183      //freopen("test.in","r+",stdin);
184      //freopen("test.out","w+",stdout);
185  
186      char s[100];
187      n=P*P*P;
188      m=4*P*P;
189      while(~scanf("%s",s))
190      {
191          if (!strcmp(s,"end"))
192              return 0;
193          Link(s);
194          Dance(0);
195      }
196      return 0;
197  }

POJ 2676  (9*9数独,和3074一样的……拿模板直接交了=。=……)

http://poj.org/problem?id=2676

POJ 2676
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <iomanip>
  6 #include <climits>
  7 #include <vector>
  8 #include <stack>
  9 #include <queue>
 10 #include <set>
 11 #include <map>
 12 #include <algorithm>
 13 #include <string>
 14 #include <cstring>
 15 
 16 using namespace std;
 17 
 18 typedef long long LL;
 19 const double EPS = 1e-11;
 20 
 21 char s[10][10];
 22 
 23 //Dancing Links
 24 //列(N+N+N)*N+N*N=4*N*N.
 25 #define N 750
 26 #define M 350
 27 const int P=9;  //p阶数独
 28 int h;
 29 int L[N*M],R[N*M],U[N*M],D[N*M],S[M],C[N*M],O[N];
 30 bool d[N][M];
 31 
 32 int n,m;
 33 inline void remove (const int &c)
 34 {
 35     L[R[c]]=L[c];
 36     R[L[c]]=R[c];
 37     for (int i=D[c];i!=c;i=D[i])
 38         for (int j=R[i];j!=i;j=R[j])
 39         {
 40             U[D[j]]=U[j];
 41             D[U[j]]=D[j];
 42             --S[C[j]];
 43         }
 44 }
 45 
 46 inline void resume (const int &c)
 47 {
 48     for (int i=U[c];i!=c;i=U[i])
 49         for (int j=L[i];j!=i;j=L[j])
 50         {
 51             U[D[j]]=j;
 52             D[U[j]]=j;
 53             ++S[C[j]];
 54         }
 55     L[R[c]]=c;
 56     R[L[c]]=c;
 57 }
 58 
 59 bool Dance(int k)
 60 {
 61     if (R[h]==h)
 62     {
 63         sort(O,O+k);
 64         for (int i=0;i<k;i++)
 65         {
 66             printf("%d",(O[i]-1)/m%P==0?P:((O[i]-1)/m%P));
 67             if ((i+1)%P==0)
 68                 printf("\n");
 69         }
 70 
 71         return true;
 72     }
 73 
 74     int c,ss=INT_MAX;
 75 
 76     for (int i=R[h];i!=h;i=R[i])
 77         if (S[i]<ss)
 78         {
 79             ss=S[i];
 80             c=i;
 81         }
 82 
 83     remove(c);
 84     for (int i=D[c];i!=c;i=D[i])
 85     {
 86         O[k]=i;
 87         for (int j=R[i];j!=i;j=R[j])
 88             remove(C[j]);
 89         if (Dance(k+1))
 90             return true;
 91         for (int j=L[i];j!=i;j=L[j])
 92             resume(C[j]);
 93     }
 94     resume(c);
 95 
 96     return false;
 97 }
 98 
 99 //Initialize
100 void Link()
101 {
102 
103     memset(d,0,sizeof(d));
104     memset(O,0,sizeof(O));
105 
106     //preprocess the sudoku 数独转换精确覆盖问题矩阵形式
107     for (int i=1;i<=P;i++)
108         for (int j=1;j<=P;j++)
109         {
110             if (s[i-1][j-1]=='0')
111             {
112                 for (int k=1;k<=P;k++)
113                 {
114                     int rr=((i-1)*P+j-1)*P+k;                   //表示数独第i行第j列数填k
115                     d[rr][(i-1)*P+k]=1;                         //表示数独第i行有数k
116                     d[rr][(j-1)*P+k+P*P]=1;                     //表示数独第j行有数k
117                     d[rr][((i-1)/3*3+(j-1)/3)*P+k+2*P*P]=1;     //表示数独第p个十六宫格有数k
118                     d[rr][(i-1)*P+j+3*P*P]=1;                   //表示数独第i行第j行有一个数(防止一个格子填多个数)
119                 }
120             }
121             else
122             {
123                 int num=s[i-1][j-1]-'0';
124                 int rr=((i-1)*P+j-1)*P+num;                 //表示数独第i行第j列数填k
125                 d[rr][(i-1)*P+num]=1;                       //表示数独第i行有数k
126                 d[rr][(j-1)*P+num+P*P]=1;                   //表示数独第j行有数k
127                 d[rr][((i-1)/3*3+(j-1)/3)*P+num+2*P*P]=1;   //表示数独第p个十六宫格有数k
128                 d[rr][(i-1)*P+j+3*P*P]=1;                   //表示数独第i行第j行有一个数(防止一个格子填多个数)
129             }
130 
131         }
132 
133     //Initialize the all matrix to list.
134     int x[N],row[N],col[M];           //x表示当前行中的第一个链,row表示当前行中上一个插入的链、col表示当前列中上一个插入的链。
135     h=0;
136     for (int i=1;i<=m;i++)
137     {
138         R[i-1]=i;
139         L[i]=i-1;
140         S[i]=0;
141         col[i]=i;
142     }
143     col[0]=0;
144     L[h]=m;
145     R[m]=h;
146 
147     for (int i=1;i<=n;i++)
148     {
149         x[i]=0;                             //行第一个链表
150         for (int j=1;j<=m;j++)
151             if (d[i][j])
152             {
153                 int index=i*(4*P*P)+j;      //带插入的列表下标。
154                 if (!x[i])
155                 {
156                     row[i]=x[i]=index;
157                 }
158                 else
159                 {
160                     R[row[i]]=index;
161                     L[index]=row[i];
162                 }
163                 D[col[j]]=index;
164                 U[index]=col[j];
165                 row[i]=col[j]=index;
166                 C[index]=j;
167                 S[j]++;
168             }
169     }
170 
171     for (int i=1;i<=n;i++)
172         if (x[i])
173         {
174             L[x[i]]=row[i];
175             R[row[i]]=x[i];
176         }
177     for (int j=1;j<=m;j++)
178     {
179         D[col[j]]=j;
180         U[j]=col[j];
181     }
182 }
183 
184 int main()
185 {
186     //freopen("test.in","r+",stdin);
187     //freopen("test.out","w+",stdout);
188 
189     int t;
190     scanf("%d",&t);
191     n=P*P*P;
192     m=4*P*P;
193     memset(s,0,sizeof(s));
194     while(t--)
195     {
196         for (int i=0;i<P;i++)
197             for (int j=0;j<P;j++)
198                 scanf("%1s",&s[i][j]);
199         Link();
200         Dance(0);
201     }
202     return 0;
203 }

POJ 3076  (16*16数独,较难)

http://poj.org/problem?id=3076

跟9*9数独比不仅仅是变个阶数那么简单。。。好多人(包括我)套模板直接MLE。。。不能用矩阵存了~得换个方式,具体的还没研究。。。

但是在网上还是找到套模板过了的(靠!)……实在找不到我的程序和他的程序比到底哪儿又耗内存了。。。求大牛指点迷津Orz……

POJ3076---我的MLE代码
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <iomanip>
  6 #include <climits>
  7 #include <vector>
  8 #include <stack>
  9 #include <queue>
 10 #include <set>
 11 #include <map>
 12 #include <algorithm>
 13 #include <string>
 14 #include <cstring>
 15 
 16 using namespace std;
 17 
 18 typedef long long LL;
 19 const double EPS = 1e-11;
 20 
 21 char s[20][20];
 22 
 23 //Dancing Links
 24 //列(N+N+N)*N+N*N=4*N*N.
 25 #define N 4100   //>16*16*16
 26 #define M 1050   //>4*16*16
 27 const int P=16;  //p阶数独
 28 int h;
 29 int L[N*M],R[N*M],U[N*M],D[N*M],S[M],C[N*M],O[N];
 30 bool d[N][M];
 31 
 32 int n,m;
 33 inline void remove (const int &c)
 34 {
 35     L[R[c]]=L[c];
 36     R[L[c]]=R[c];
 37     for (int i=D[c];i!=c;i=D[i])
 38         for (int j=R[i];j!=i;j=R[j])
 39         {
 40             U[D[j]]=U[j];
 41             D[U[j]]=D[j];
 42             --S[C[j]];
 43         }
 44 }
 45 
 46 inline void resume (const int &c)
 47 {
 48     for (int i=U[c];i!=c;i=U[i])
 49         for (int j=L[i];j!=i;j=L[j])
 50         {
 51             U[D[j]]=j;
 52             D[U[j]]=j;
 53             ++S[C[j]];
 54         }
 55     L[R[c]]=c;
 56     R[L[c]]=c;
 57 }
 58 
 59 bool Dance(int k)
 60 {
 61     if (R[h]==h)
 62     {
 63         sort(O,O+k);
 64         for (int i=0;i<k;i++)
 65         {
 66             printf("%c",(O[i]-1)/m%P==0?'P':((O[i]-1)/m%P)+'A'-1);
 67             if ((i+1)%16==0)
 68                 printf("\n");
 69         }
 70 
 71 
 72         return true;
 73     }
 74 
 75     int c,ss=INT_MAX;
 76 
 77     for (int i=R[h];i!=h;i=R[i])
 78         if (S[i]<ss)
 79         {
 80             ss=S[i];
 81             c=i;
 82         }
 83 
 84     remove(c);
 85     for (int i=D[c];i!=c;i=D[i])
 86     {
 87         O[k]=i;
 88         for (int j=R[i];j!=i;j=R[j])
 89             remove(C[j]);
 90         if (Dance(k+1))
 91             return true;
 92         for (int j=L[i];j!=i;j=L[j])
 93             resume(C[j]);
 94     }
 95     resume(c);
 96 
 97     return false;
 98 }
 99 
100 //Initialize
101 void Link()
102 {
103 
104     memset(d,0,sizeof(d));
105     memset(O,0,sizeof(O));
106 
107     //preprocess the sudoku 数独转换精确覆盖问题矩阵形式
108     for (int i=1;i<=P;i++)
109         for (int j=1;j<=P;j++)
110         {
111             if (s[i-1][j-1]=='-')
112             {
113                 for (int k=1;k<=P;k++)
114                 {
115                     int rr=((i-1)*P+j-1)*P+k;                   //表示数独第i行第j列数填k
116                     d[rr][(i-1)*P+k]=1;                         //表示数独第i行有数k
117                     d[rr][(j-1)*P+k+P*P]=1;                     //表示数独第j行有数k
118                     d[rr][((i-1)/4*4+(j-1)/4)*P+k+2*P*P]=1;     //表示数独第p个十六宫格有数k
119                     d[rr][(i-1)*P+j+3*P*P]=1;                   //表示数独第i行第j行有一个数(防止一个格子填多个数)
120                 }
121             }
122             else
123             {
124                 int num=s[i-1][j-1]-'A'+1;
125                 int rr=((i-1)*P+j-1)*P+num;                 //表示数独第i行第j列数填k
126                 d[rr][(i-1)*P+num]=1;                       //表示数独第i行有数k
127                 d[rr][(j-1)*P+num+P*P]=1;                   //表示数独第j行有数k
128                 d[rr][((i-1)/4*4+(j-1)/4)*P+num+2*P*P]=1;   //表示数独第p个十六宫格有数k
129                 d[rr][(i-1)*P+j+3*P*P]=1;                   //表示数独第i行第j行有一个数(防止一个格子填多个数)
130             }
131 
132         }
133 
134     //Initialize the all matrix to list.
135     int x[N],row[N],col[M];           //x表示当前行中的第一个链,row表示当前行中上一个插入的链、col表示当前列中上一个插入的链。
136     h=0;
137     for (int i=1;i<=m;i++)
138     {
139         R[i-1]=i;
140         L[i]=i-1;
141         S[i]=0;
142         col[i]=i;
143     }
144     col[0]=0;
145     L[h]=m;
146     R[m]=h;
147 
148     for (int i=1;i<=n;i++)
149     {
150         x[i]=0;                             //行第一个链表
151         for (int j=1;j<=m;j++)
152             if (d[i][j])
153             {
154                 int index=i*(4*P*P)+j;      //带插入的列表下标。
155                 if (!x[i])
156                 {
157                     row[i]=x[i]=index;
158                 }
159                 else
160                 {
161                     R[row[i]]=index;
162                     L[index]=row[i];
163                 }
164                 D[col[j]]=index;
165                 U[index]=col[j];
166                 row[i]=col[j]=index;
167                 C[index]=j;
168                 S[j]++;
169             }
170     }
171 
172     for (int i=1;i<=n;i++)
173         if (x[i])
174         {
175             L[x[i]]=row[i];
176             R[row[i]]=x[i];
177         }
178     for (int j=1;j<=m;j++)
179     {
180         D[col[j]]=j;
181         U[j]=col[j];
182     }
183 }
184 
185 int main()
186 {
187     //freopen("test.in","r+",stdin);
188     //freopen("test.out","w+",stdout);
189 
190 
191     n=P*P*P;
192     m=4*P*P;
193     memset(s,0,sizeof(s));
194     while(scanf("%1s",&s[0][0])!=EOF)
195     {
196 
197         for (int j=1;j<16;j++)
198             scanf("%1s",&s[0][j]);
199         for (int i=1;i<16;i++)
200             for (int j=0;j<16;j++)
201                 scanf("%1s",&s[i][j]);
202         Link();
203         Dance(0);
204     }
205     return 0;
206 }
POJ3076 套模板AC代码
  1 #include <cstdio>
  2 #include <cstring>
  3 #define inf 100000000
  4 #define N 256*16
  5 #define M 256*4
  6 #define MAX N*M
  7 int s[M],mat[N][M],ansq[N],u[MAX],d[MAX],l[MAX],r[MAX],c[MAX],row[MAX];
  8 int deep;
  9 void build(int n,int m)
 10 {
 11     r[0]=1;
 12     l[0]=m;
 13     for(int i=1; i<=m; i++)
 14     {
 15         l[i]=i-1;
 16         r[i]=(i+1)%(m+1);
 17         c[i]=d[i]=u[i]=i;
 18         s[i]=0;
 19     }
 20     int size=m;
 21     for(int i=1; i<=n; i++)
 22     {
 23         int rp=0;
 24         for(int j=1; j<=m; j++)
 25             if(mat[i-1][j-1])
 26             {
 27                 size++;
 28                 d[u[j]]=size;
 29                 u[size]=u[j];
 30                 d[size]=j;
 31                 u[j]=size;
 32                 if(!rp)
 33                 {
 34                     rp=size;
 35                     l[size]=size;
 36                     r[size]=size;
 37                 }
 38                 else
 39                 {
 40                     l[size]=l[rp];
 41                     r[size]=rp;
 42                     r[l[rp]]=size;
 43                     l[rp]=size;
 44                 }
 45                 c[size]=j;
 46                 row[size]=i;
 47                 s[j]++;
 48             }
 49     }
 50 }
 51 inline void remove(int pc)
 52 {
 53     r[l[pc]]=r[pc];
 54     l[r[pc]]=l[pc];
 55     for(int i=d[pc]; i!=pc; i=d[i])
 56         for(int j=r[i]; j!=i; j=r[j])
 57         {
 58             u[d[j]]=u[j];
 59             d[u[j]]=d[j];
 60             s[c[j]]--;
 61         }
 62 }
 63  
 64 inline void resume(int pc)
 65 {
 66     for(int i=u[pc]; i!=pc; i=u[i])
 67         for(int j=l[i]; j!=i; j=l[j])
 68         {
 69             d[u[j]]=j;
 70             u[d[j]]=j;
 71             s[c[j]]++;
 72         }
 73     l[r[pc]]=pc;
 74     r[l[pc]]=pc;
 75 }
 76  
 77 bool dfs(int dep)
 78 {
 79     if(!r[0])
 80     {
 81         deep = dep;
 82         return true;
 83     }
 84  
 85     int pc,mins=inf;
 86     for(int t=r[0]; t; t=r[t])
 87         if(mins>s[t])
 88         {
 89             mins=s[t];
 90             pc=t;
 91         }
 92     remove(pc);
 93     for(int i=d[pc]; i!=pc; i=d[i])
 94     {
 95         ansq[dep]=row[i]-1;
 96         for(int j=r[i]; j!=i; j=r[j])
 97             remove(c[j]);
 98         if(dfs(dep+1))
 99             return true;
100         for(int j=l[i]; j!=i; j=l[j])
101             resume(c[j]);
102     }
103     resume(pc);
104     return false;
105 }
106 char str[20];
107 char sudoku[17][17];
108 int main()
109 {
110     while(1)
111     {
112         memset(mat,0,sizeof(mat));
113         memset(ansq,0,sizeof(ansq));
114         memset(sudoku,0,sizeof(sudoku));
115         for(int i=0; i<16; i++)
116         {
117             if(scanf("%s",str)!=1)
118                 return 0;
119             for(int j=0; j<16; j++)
120             {
121                 if(str[j]=='-')
122                 {
123                     for(int k=0; k<16; k++)
124                     {
125                         mat[256*k+16*i+j][k*16+i]=1;
126                         mat[256*k+16*i+j][256+k*16+j]=1;
127                         mat[256*k+16*i+j][512+16*k+i/4*4+j/4]=1;
128                         mat[256*k+16*i+j][768+i*16+j]=1;
129                     }
130                 }
131                 else
132                 {
133                     mat[256*(str[j]-'A')+16*i+j][(str[j]-'A')*16+i]=1;
134                     mat[256*(str[j]-'A')+16*i+j][256+(str[j]-'A')*16+j]=1;
135                     mat[256*(str[j]-'A')+16*i+j][512+16*(str[j]-'A')+i/4*4+j/4]=1;
136                     mat[256*(str[j]-'A')+16*i+j][768+i*16+j]=1;
137                 }
138             }
139         }
140         build(N,M);
141         dfs(0);
142         for(int i=0; i<deep; i++)
143             sudoku[ansq[i]%256/16][ansq[i]%16]=ansq[i]/256+'A';
144         for(int i=0; i<16; i++)
145             printf("%s\n",sudoku[i]);
146         printf("\n");
147     }
148     return 0;
149 }

HDU 4069  (好题~~~数独的变形---2011 ACM/ICPC Asia Regional Fuzhou Site —— Online Contest) 

http://acm.hdu.edu.cn/showproblem.php?pid=4069

原来不会判断多解。。。
dfs的时候如果成功搜索两次,说明存在多解,
成功搜索一次则存在唯一解,
否则无解。
用一个Floodfill预处理每一个格子所属块。
HDU 4069
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <iomanip>
  6 #include <climits>
  7 #include <vector>
  8 #include <stack>
  9 #include <queue>
 10 #include <set>
 11 #include <map>
 12 #include <algorithm>
 13 #include <string>
 14 #include <cstring>
 15 
 16 using namespace std;
 17 
 18 typedef long long LL;
 19 const double EPS = 1e-11;
 20 
 21 int s[10][10];
 22 int color[10][10];
 23 
 24 //Dancing Links
 25 //列(N+N+N)*N+N*N=4*N*N.
 26 #define N 750
 27 #define M 350
 28 const int P=9;  //p阶数独
 29 int h;
 30 int ans;        //判断多解
 31 int L[N*M],R[N*M],U[N*M],D[N*M],S[M],C[N*M],O[N];
 32 bool d[N][M];
 33 
 34 
 35 int n,m;
 36 inline void remove (const int &c)
 37 {
 38     L[R[c]]=L[c];
 39     R[L[c]]=R[c];
 40     for (int i=D[c];i!=c;i=D[i])
 41         for (int j=R[i];j!=i;j=R[j])
 42         {
 43             U[D[j]]=U[j];
 44             D[U[j]]=D[j];
 45             --S[C[j]];
 46         }
 47 }
 48 
 49 inline void resume (const int &c)
 50 {
 51     for (int i=U[c];i!=c;i=U[i])
 52         for (int j=L[i];j!=i;j=L[j])
 53         {
 54             U[D[j]]=j;
 55             D[U[j]]=j;
 56             ++S[C[j]];
 57         }
 58     L[R[c]]=c;
 59     R[L[c]]=c;
 60 }
 61 
 62 bool Dance(int k)
 63 {
 64     if (R[h]==h)
 65     {
 66         ans++;
 67         if (ans==2)
 68             return true;
 69         return false;
 70     }
 71 
 72     int c,ss=INT_MAX;
 73 
 74     for (int i=R[h];i!=h;i=R[i])
 75         if (S[i]<ss)
 76         {
 77             ss=S[i];
 78             c=i;
 79         }
 80 
 81     remove(c);
 82     for (int i=D[c];i!=c;i=D[i])
 83     {
 84         if (ans==0) O[k]=i;
 85         for (int j=R[i];j!=i;j=R[j])
 86             remove(C[j]);
 87         if (Dance(k+1))
 88             return true;
 89         for (int j=L[i];j!=i;j=L[j])
 90             resume(C[j]);
 91     }
 92     resume(c);
 93 
 94     return false;
 95 }
 96 
 97 //Initialize
 98 void Link()
 99 {
100 
101     memset(d,0,sizeof(d));
102     memset(O,0,sizeof(O));
103 
104     //preprocess the sudoku 数独转换精确覆盖问题矩阵形式
105     for (int i=1;i<=P;i++)
106         for (int j=1;j<=P;j++)
107         {
108             if (s[i-1][j-1]==0)
109             {
110                 for (int k=1;k<=P;k++)
111                 {
112                     int rr=((i-1)*P+j-1)*P+k;                   //表示数独第i行第j列数填k
113                     d[rr][(i-1)*P+k]=1;                         //表示数独第i行有数k
114                     d[rr][(j-1)*P+k+P*P]=1;                     //表示数独第j行有数k
115                     d[rr][(color[i-1][j-1]-1)*P+k+2*P*P]=1;     //表示数独第p个十六宫格有数k
116                     d[rr][(i-1)*P+j+3*P*P]=1;                   //表示数独第i行第j行有一个数(防止一个格子填多个数)
117                 }
118             }
119             else
120             {
121                 int num=s[i-1][j-1];
122                 int rr=((i-1)*P+j-1)*P+num;                 //表示数独第i行第j列数填k
123                 d[rr][(i-1)*P+num]=1;                       //表示数独第i行有数k
124                 d[rr][(j-1)*P+num+P*P]=1;                   //表示数独第j行有数k
125                 d[rr][(color[i-1][j-1]-1)*P+num+2*P*P]=1;      //表示数独第p个十六宫格有数k
126                 d[rr][(i-1)*P+j+3*P*P]=1;                   //表示数独第i行第j行有一个数(防止一个格子填多个数)
127             }
128 
129         }
130 
131     //Initialize the all matrix to list.
132     int x[N],row[N],col[M];           //x表示当前行中的第一个链,row表示当前行中上一个插入的链、col表示当前列中上一个插入的链。
133     h=0;
134     for (int i=1;i<=m;i++)
135     {
136         R[i-1]=i;
137         L[i]=i-1;
138         S[i]=0;
139         col[i]=i;
140     }
141     col[0]=0;
142     L[h]=m;
143     R[m]=h;
144 
145     for (int i=1;i<=n;i++)
146     {
147         x[i]=0;                             //行第一个链表
148         for (int j=1;j<=m;j++)
149             if (d[i][j])
150             {
151                 int index=i*(4*P*P)+j;      //带插入的列表下标。
152                 if (!x[i])
153                 {
154                     row[i]=x[i]=index;
155                 }
156                 else
157                 {
158                     R[row[i]]=index;
159                     L[index]=row[i];
160                 }
161                 D[col[j]]=index;
162                 U[index]=col[j];
163                 row[i]=col[j]=index;
164                 C[index]=j;
165                 S[j]++;
166             }
167     }
168 
169     for (int i=1;i<=n;i++)
170         if (x[i])
171         {
172             L[x[i]]=row[i];
173             R[row[i]]=x[i];
174         }
175     for (int j=1;j<=m;j++)
176     {
177         D[col[j]]=j;
178         U[j]=col[j];
179     }
180 }
181 
182 int dr[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
183 int vis[10][10];
184 int block[10][10][10][10];
185 int nu;
186 
187 void dfs(int x,int y)
188 {
189     color[x][y]=nu;
190     vis[x][y]=1;
191     for (int i=0;i<4;i++)
192     {
193         int dx=x+dr[i][0];
194         int dy=y+dr[i][1];
195         if (!vis[dx][dy] && !block[dx][dy][x][y] && dx>=0 && dx<9 && dy>=0 && dy<9)
196             dfs(dx,dy);
197     }
198 }
199 
200 int main()
201 {
202     //freopen("test.in","r+",stdin);
203     //freopen("test.out","w+",stdout);
204 
205     int t;
206     int caseo=1;
207     scanf("%d",&t);
208     n=P*P*P;
209     m=4*P*P;
210     memset(s,0,sizeof(s));
211     while(t--)
212     {
213         memset(block,0,sizeof(block));
214         memset(color,0,sizeof(color));
215         memset(vis,0,sizeof(vis));
216         ans=0;
217         nu=1;
218         for (int i=0;i<P;i++)
219             for (int j=0;j<P;j++)
220             {
221                 scanf("%d",&s[i][j]);
222                 if (s[i][j]>=128)
223                 {
224                     if (j>0)
225                     {
226                         block[i][j][i][j-1]=1;
227                         block[i][j-1][i][j]=1;
228                     }
229                     s[i][j]-=128;
230                 }
231                 if (s[i][j]>=64)
232                 {
233                     if (i<8)
234                     {
235                         block[i][j][i+1][j]=1;
236                         block[i+1][j][i][j]=1;
237                     }
238                     s[i][j]-=64;
239                 }
240                 if (s[i][j]>=32)
241                 {
242                     if (j<8)
243                     {
244                         block[i][j][i][j+1]=1;
245                         block[i][j+1][i][j]=1;
246                     }
247 
248                     s[i][j]-=32;
249                 }
250                 if (s[i][j]>=16)
251                 {
252                     if (i>0)
253                     {
254                         block[i][j][i-1][j]=1;
255                         block[i-1][j][i][j]=1;
256                     }
257                     s[i][j]-=16;
258                 }
259             }
260 
261 
262         for (int i=0;i<P;i++)
263             for (int j=0;j<P;j++)
264             {
265                 if (!vis[i][j])
266                 {
267                     dfs(i,j);
268                     nu++;
269                 }
270             }
271         Link();
272         Dance(0);
273         printf("Case %d:\n",caseo++);
274         if (ans==1)
275         {
276             sort(O,O+81);
277             for (int i=0;i<81;i++)
278             {
279                 printf("%d",(O[i]-1)/m%P==0?P:((O[i]-1)/m%P));
280                 if ((i+1)%P==0)
281                     printf("\n");
282             }
283         }
284         else if (ans==2)
285             printf("Multiple Solutions\n");
286         else
287             printf("No solution\n");
288     }
289     return 0;
290 }

(未完待续。。。)

 

举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG
原文地址:https://www.cnblogs.com/AbandonZHANG/p/2660188.html