poj3074-舞蹈链

题目链接:http://poj.org/problem?id=3074

Description
In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids.

. 2 7 3 8 . . 1 .
. 1 . . . 6 7 3 5
. . . . . . . 2 9
3 . 5 6 9 2 . 8 .
. . . . . . . . .
. 6 . 1 7 4 5 . 3
6 4 . . . . . . .
9 5 1 8 . . . 7 .
. 8 . . 6 5 3 4 .
Given some of the numbers in the grid, your goal is to determine the remaining numbers such that the numbers 1 through 9 appear exactly once in (1) each of nine 3 × 3 subgrids, (2) each of the nine rows, and (3) each of the nine columns.

Input
The input test file will contain multiple cases. Each test case consists of a single line containing 81 characters, which represent the 81 squares of the Sudoku grid, given one row at a time. Each character is either a digit (from 1 to 9) or a period (used to indicate an unfilled square). You may assume that each puzzle in the input will have exactly one solution. The end-of-file is denoted by a single line containing the word “end”.

Output
For each test case, print a line representing the completed Sudoku puzzle.

Sample Input
.2738..1..1…6735…….293.5692.8………..6.1745.364…….9518…7..8..6534.
……52..8.4……3…9…5.1…6..2..7……..3…..6…1……….7.4…….3.
end

Sample Output
527389416819426735436751829375692184194538267268174593643217958951843672782965341
416837529982465371735129468571298643293746185864351297647913852359682714128574936

题意
求解数独

思路
直接bfs好像要t(没试过..),舞蹈链精确覆盖的经典题目,一个数独每个位置的要求,可以得到以下四个约束
1.每个位置有且只有一个数字
2.每个位置的数字在一行只能出现一次
3.每个位置的数字在一列只能出现一次
4.每个位置的数字在一个宫格内只能出现一次
然后针对每个位置可以建立舞蹈链了
前81列,为1条件的约束
82-162列,为2条件的约束
163-243列,为3条件的约束
244-324列,为4条件的约束
则舞蹈链行数为确定的点数+未确定的点数9,列数为324。
如果i行j列为数k,对应行则在(i - 1)
9 + j列,(i - 1) 9 + k + 81列, (j - 1) 9 + k + 162列,(((i - 1) / 3) 3 + ((j + 2) / 3) - 1) 9 + k + 243列建立。
如果i行j列为未知数,则k为1-9,对于每个k都应该建立行列关系。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
//最大行数
const int MN = 1010;
//最大列数
const int MM = 500;
//最大点数
const int MNN = MN * MM + 10;
struct DLX
{
//一共n行m列,s个节点
int n, m, size;
//交叉十字链表组成部分
//第i个节点的上U下D左L右R,所在位置row行col列
int U[MNN], D[MNN], L[MNN], R[MNN], row[MNN], col[MNN];
//H数组记录行选择指针,S数组记录覆盖个数
int H[MNN], S[MM];
//res记录行个数,ans数组记录可行解
int ansd, ans[MN];
//初始化空表
void init(int x, int y)
{
n = x, m = y;
//其中0节点作为head节点,其他作为列首节点
for (int i = 0; i <= m; ++i) {
U[i] = D[i] = i;
L[i] = i - 1;
R[i] = i + 1;
}
R[m] = 0; L[0] = m;
size = m;
memset(S, 0, sizeof(S));
memset(H, -1, sizeof(H));
}
void Link(int r, int c)
{
//节点数加一,设置s节点所处位置,以及S列覆盖个数加一
size++; row[size] = r; col[size] = c; S[c]++;
//将s节点插入对应列中
D[size] = D[c]; U[D[c]] = size;
U[size] = c; D[c] = size;
if (H[r] < 0) {//如果该行没有元素,H[r]标记该行起始节点
H[r] = L[size] = R[size] = size;
}
else {
//将该节点插入该行第一个节点后面
R[size] = R[H[r]];
L[R[H[r]]] = size;
L[size] = H[r];
R[H[r]] = size;
}
}
//精确覆盖
void remove(int c)
{
//删除c列
L[R[c]] = L[c]; R[L[c]] = R[c];
//删除该列上的元素对应的行
for (int i = D[c]; i != c; i = D[i]) {//枚举该列元素
for (int j = R[i]; j != i; j = R[j]) {//枚举列的某个元素所在行遍历
U[D[j]] = U[j];
D[U[j]] = D[j];
//将该列上的S数组减一
--S[col[j]];
}
}
}
void resume(int c)
{
//恢复c列
for (int i = U[c]; i != c; i = U[i]) {//枚举该列元素
for (int j = L[i]; j != i; j = L[j]) {
U[D[j]] = j; D[U[j]] = j;
++S[col[j]];
}
}
L[R[c]] = c; R[L[c]] = c;
}
bool dancing(int deep)
{
//if (ansd < deep) return false;
//当矩阵为空时,说明找到一个可行解,算法终止
if (R[0] == 0) {
//ansd = min(ansd, deep);
ansd = deep;
return true;
}
//找到节点数最少的列,枚举这列上的所有行
int c = R[0];
for (int i = R[0]; i != 0; i = R[i]) {
if (S[i] < S[c]) {
c = i;
}
}
//删除节点数最少的列
remove(c);
for (int i = D[c]; i != c; i = D[i]) {
//将行r放入当前解
ans[deep] = row[i];
//行上节点对应的列上进行删除
for (int j = R[i]; j != i; j = R[j])
remove(col[j]);

//进入下一层
if (dancing(deep + 1))return true;
//对行上的节点对应的列进行恢复
for (int j = L[i]; j != i; j = L[j])
resume(col[j]);
}
//恢复节点数最少列
resume(c);
return false;
}

//重复覆盖
//将列与矩阵完全分开
void Remove(int c)
{
for (int i = D[c]; i != c; i = D[i]) {
L[R[i]] = L[i];
R[L[i]] = R[i];
}
}
void Resume(int c)
{
for (int i = D[c]; i != c; i = D[i]) {
L[R[i]] = R[L[i]] = i;
}
}
int vis[MNN];
//估价函数,模拟删除列,H(),函数返回的是至少还需要多少行才能完成重复覆盖
int A()
{
int dis = 0;
for (int i = R[0]; i != 0; i = R[i]) vis[i] = 0;
for (int i = R[0]; i != 0; i = R[i]) {
if (!vis[i]) {
dis++; vis[i] = 1;
for (int j = D[i]; j != i; j = D[j]) {
for (int k = R[j]; k != j; k = R[k]) {
vis[col[k]] = 1;
}
}
}
}
return dis;
}
void dance(int deep)
{
if (!R[0]) {
//cout << res << endl;
ansd = deep;
return;
}
if (deep + A() >= ansd) return;
int c = R[0];
for (int i = R[0]; i != 0; i = R[i]) {
if (S[i] < S[c]) {
c = i;
}
}
for (int i = D[c]; i != c; i = D[i]) {
//每次将第i列其他节点删除,只保留第i节点,为了找该行的节点
Remove(i);
//将列上的节点完全与矩阵脱离,只删列首节点是不行的
for (int j = R[i]; j != i; j = R[j]) {
Remove(j);
}
dance(deep + 1);
for (int j = L[i]; j != i; j = L[j]) {
Resume(j);
}
Resume(i);
}
}
};
DLX dzl;
int mp[15][15];
int x[1010];
int y[1010];
int kk[1010];
int main() {
char s[100];
while (cin >> s) {
if (strcmp(s, "end") == 0)
break;
for (int i = 0; i < 81; i++) {
int xx = i / 9;
int yy = i % 9;
if (s[i] == '.')
mp[xx + 1][yy + 1] = 0;
else
mp[xx + 1][yy + 1] = s[i] - '0';
}
dzl.init(800, 324);
int len = 1;
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= 9; j++) {
if (mp[i][j] == 0) {
for (int k = 1; k <= 9; k++) {
dzl.Link(len, (i - 1) * 9 + j);
dzl.Link(len, (i - 1) * 9 + k + 81);
dzl.Link(len, (j - 1) * 9 + k + 162);
dzl.Link(len, (((i - 1) / 3) * 3 + ((j + 2) / 3) - 1) * 9 + k + 243);
x[len] = i, y[len] = j, kk[len] = k;
len++;
}
}
else {
int k = mp[i][j];
dzl.Link(len, (i - 1) * 9 + j);
dzl.Link(len, (i - 1) * 9 + k + 81);
dzl.Link(len, (j - 1) * 9 + k + 162);
dzl.Link(len, (((i - 1) / 3) * 3 + ((j + 2) / 3) - 1) * 9 + k + 243);
x[len] = i, y[len] = j, kk[len] = k;
len++;
}
}
}
dzl.dancing(0);
//cout << dzl.ansd << endl;
for (int i = 0; i < dzl.ansd; i++) {
int r = dzl.ans[i];
mp[x[r]][y[r]] = kk[r];
}
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= 9; j++) {
cout << mp[i][j];
}
}
cout << endl;
}
//system("pause");
}

谢谢你请我吃糖果

你可能感兴趣的:(poj3074-舞蹈链)