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?例如,下面这个矩阵

(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 }

 

 

(未完待续。。。)

 

你可能感兴趣的:(link)