参考文章:
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++模板:
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
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
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
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……
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 }
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
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 }
(未完待续。。。)