poj3074 Sudoku 转化为精确覆盖问题 DLX

Sudoku

终于搞定了,开敲以前,我还在位建图而发愁呢,呵呵

DLX的经典应用:解决数独问题

momodi的论文写的真是不错

建图方式(就是01矩阵)如下

行:
一共9 * 9 * 9 == 729行。一共9 * 9小格,每一格有9种可能性(1 - 9),每一种可能都对应着一行。
列:

一共(9 + 9 + 9) * 9 + 81 == 324 种前面三个9分别代表着9行9列和9小块,乘以9的意思是9种可能(1 - 9),因为每种可能只可以选择一个。81代表着81个小格,限制着每一个小格只放一个数字。

 

读入数据后,如果为'.',则建9行,即有1-9种可能,否则建一行,表示某小格只能放确定的某个数字。

建完图,加一份模板就可以过了。

代码
 
   
1 /* DLX解决sudoku问题,转化为729*324的精确覆盖问题 */
2 #include < stdio.h >
3 #include < string .h >
4   #define INF 0x3fffffff
5   #define NN 330
6 #define MM 740
7 int N, M;
8 int cntc[NN];
9 int L[NN * MM], R[NN * MM], U[NN * MM], D[NN * MM], C[NN * MM];
10 int head;
11 int adj[MM][NN];
12 int ans[ 10 ][ 10 ];
13
14 /* 删除第c列 */
15 void remove( int c){
16 L[R[c]] = L[c];
17 R[L[c]] = R[c];
18
19 int i, j;
20 for (i = D[c]; i != c; i = D[i]){
21 for (j = R[i]; j != i; j = R[j]){
22 U[D[j]] = U[j];
23 D[U[j]] = D[j];
24 cntc[C[j]] -- ;
25 }
26 }
27 }
28 /* 恢复第c列 */
29 void resume( int c){
30 L[R[c]] = c;
31 R[L[c]] = c;
32
33 int i, j;
34 for (i = D[c]; i != c; i = D[i]){
35 for (j = R[i]; j != i; j = R[j]){
36 U[D[j]] = j;
37 D[U[j]] = j;
38 cntc[C[j]] ++ ;
39 }
40 }
41 }
42 int dfs(){
43
44 if (R[head] == head) return 1 ;
45
46 int min = INF;
47 int c, i, j;
48 for (i = R[head]; i != head; i = R[i]){
49 if (cntc[i] < min){
50 c = i;
51 min = cntc[i];
52 }
53 }
54 remove(c);
55 for (i = D[c]; i != c; i = D[i]){
56 // O[idx++] = (i - 1) / M;
57 int r = (i - 1 ) / N;
58 int num = (r + 8 ) / 9 ;
59 int key = r % 9 ;
60 if (key == 0 ) key = 9 ;
61 int x = (num + 8 ) / 9 ;
62 int y = num % 9 ;
63 if (y == 0 ) y = 9 ;
64 ans[x][y] = key;
65
66 for (j = R[i]; j != i; j = R[j]){
67 remove(C[j]);
68 }
69 if (dfs()) return 1 ;
70 /* 这个顺序很重要,删除和恢复的方向必须相反
71 开始相同,都是向右的,结果TLE了 */
72 for (j = L[i]; j != i; j = L[j]){
73 resume(C[j]);
74 }
75 }
76 resume(c);
77 return 0 ;
78 }
79
80 /* 建图 */
81 int Build(){
82 int i, j, now, pre, first;
83 head = 0 ;
84 for (j = head; j < N; j ++ ){
85 R[j] = j + 1 ;
86 L[j + 1 ] = j;
87 }
88 L[head] = j;
89 R[j] = head;
90
91 /* 列双向链表 */
92 for (j = 1 ; j <= N; j ++ ){
93 pre = j;
94 cntc[j] = 0 ;
95 for (i = 1 ; i <= M; i ++ ){
96 if (adj[i][j]){
97 now = i * N + j;
98 C[now] = j;
99 cntc[j] ++ ;
100 D[pre] = now;
101 U[now] = pre;
102 pre = now;
103 }
104 }
105 now = j;
106 D[pre] = now;
107 U[now] = pre;
108 if (cntc[j] == 0 ) return 0 ;
109 }
110 /* 行双向链表 */
111 for (i = 1 ; i <= M; i ++ ){
112 pre = first = - 1 ;
113 for (j = 1 ; j <= N; j ++ ){
114 if (adj[i][j]){
115 now = i * N + j;
116 if (pre != - 1 ){
117 R[pre] = now;
118 L[now] = pre;
119 } else {
120 first = now;
121 }
122 pre = now;
123 }
124 }
125 if (first != - 1 ){
126 now = first;
127 R[pre] = now;
128 L[now] = pre;
129 }
130 }
131 return 1 ;
132 }
133
134 int main()
135 {
136 char str[ 85 ];
137 int i, j, k;
138 while (scanf( " %s " , str) != EOF){
139 if (strcmp(str, " end " ) == 0 ) break ;
140
141 memset(adj, 0 , sizeof (adj));
142 for (i = 1 ; i <= 9 ; i ++ ){
143 for (j = 1 ; j <= 9 ; j ++ ){
144 int t = 9 * (i - 1 ) + j;
145 // for (l = 1; l <= 9; l++){
146 // adj[9 * (t - 1) + l][t] = 1;
147 if (str[t - 1 ] == ' . ' ){
148 for (k = 1 ; k <= 9 ; k ++ ){
149 // adj[t][k] = 1;
150 adj[ 9 * (t - 1 ) + k][t] = 1 ;
151 adj[ 9 * (t - 1 ) + k][ 81 + (i - 1 ) * 9 + k] = 1 ; // row
152 adj[ 9 * (t - 1 ) + k][ 162 + (j - 1 ) * 9 + k] = 1 ; // col
153 adj[ 9 * (t - 1 ) + k][ 243 + ((i - 1 ) / 3 * 3 + (j + 2 ) / 3 - 1 ) * 9 + k] = 1 ; // grid
154 }
155 } else {
156 k = str[t - 1 ] - ' 0 ' ;
157 adj[ 9 * (t - 1 ) + k][t] = 1 ;
158 adj[ 9 * (t - 1 ) + k][ 81 + (i - 1 ) * 9 + k] = 1 ; // row
159 adj[ 9 * (t - 1 ) + k][ 162 + (j - 1 ) * 9 + k] = 1 ; // col
160 adj[ 9 * (t - 1 ) + k][ 243 + ((i - 1 ) / 3 * 3 + (j + 2 ) / 3 - 1 ) * 9 + k] = 1 ; // grid
161 }
162 // }
163 }
164 }
165 M = 729 ;
166 N = 324 ;
167 Build();
168 dfs();
169 for (i = 1 ; i <= 9 ; i ++ ){
170 for (j = 1 ; j <= 9 ; j ++ ) printf( " %d " , ans[i][j]);
171 }
172 puts( "" );
173 }
174 return 0 ;
175 }
176

 

转载于:https://www.cnblogs.com/ylfdrib/archive/2010/10/06/1844559.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值