poj-3074 Sudoku

考点

  • DFS
  • 状态压缩
  • 剪枝

题解

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
#include <iostream>
#include <string>
using namespace std;
const int maxn = 9;
char c[maxn][maxn];
// 每行、每列、每个九宫格的可选数字状态
int row[maxn], col[maxn], grid[maxn];
// 0~(1<<maxn)-1 每个数有多少个1
int cnt[1 << maxn];

int lowbit(int x) { return x & -x; }

int g(int x, int y) { return (x / 3) * 3 + (y / 3); }

void flip(int x, int y, int v) {
row[x] ^= (1 << v), col[y] ^= (1 << v), grid[g(x, y)] ^= (1 << v);
}

bool dfs(int n) {
// 所有点都成功搜完
if (!n) return true;
int p = 10, x, y, s;
// 先处理最小可能的点
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
if (c[i][j] != '.') continue;
s = row[i] & col[j] & grid[g(i, j)];
// 明明是'.',但是没地方放,说明本次搜索失败
if (!s) return false;
if (cnt[s] < p) p = cnt[s], x = i, y = j;
}
}
s = row[x] & col[y] & grid[g(x, y)];
while (s) {
// s的lowbit是1向左移动了v位
int v = cnt[lowbit(s) - 1];
c[x][y] = '1' + v;
flip(x, y, v);
if (dfs(n - 1)) return true;
flip(x, y, v);
c[x][y] = '.';
s -= lowbit(s);
}
return false;
}

int main() {
string str;
// 打表所有状态的1个数
for (int i = 0; i < (1 << maxn); ++i) {
int j = i;
while (j) ++cnt[i], j -= lowbit(j);
}
while (cin >> str && str[0] != 'e') {
int tot = 0;
// 初始状态下,每个点可以放0~8所有数字
for (int i = 0; i < maxn; ++i) row[i] = col[i] = grid[i] = (1 << maxn) - 1;
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
c[i][j] = str[i * 9 + j];
if (c[i][j] != '.') {
flip(i, j, c[i][j] - '1');
} else {
++tot;
}
}
}
dfs(tot);
string ans;
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
ans += c[i][j];
}
}
cout << ans << endl;
}
return 0;
}

思路

直接上图,实现的具体细节见注释就好了~