- 题目大意
数独是一种传统益智游戏,你需要把一个9 × 9的数独补充完整,使得图中每行、每列、每个3 × 3的九宫格内数字1~9均恰好出现一次。
请编写一个程序填写数独。
- 思路
搜索边界分为两种:
1.是如果所有位置都被填满,就找到了一个解.
2. 如果发现某个位置没有能填的合法数字,说明当前分支搜索失败,应该回溯去尝试其他分支.
由于本题数据比较严格, 不能像POJ2676那样一个一个尝试,所以数据上需要做一些优化。
1.在搜索时, 优先搜索决策少的。
2.每行每列每个九宫格,分别用一个9位二进制数表示,保存还可以选择的数据。
3. 对于每个位置,把它所在行、列、九宫格的3个二进制数做位与(&)运算,就可以得到该位置能填哪些数,用lowbit运算就可以把能填的数字取出。
4. 修改状态.
5. dfs搜索.
6. 回溯.
ones数组: 存每个数中有多少个1, 即是用来存当前这个格子有多少个可选择的决策,找出决策最少的格子然后进行深搜。
map数组: 存某个数子的第一个1的下标, 即是取出这个二进制数的第一个1代表的可以放到格子里面的数字。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cstdio>
using namespace std;
const int N = 9;
int row[N], col[N], cell[3][3];
int ones[1 << N], map[1 << N];
char str[100];
int lowbit(int x){
return x & (-x);
}
void init(){
for(int i = 0; i < N; i++){
row[i] = col[i] = (1 << N) - 1;
}
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++)
cell[i][j] = (1 << N) - 1;
}
}
int get(int x, int y){
return row[x] & col[y] & cell[x / 3][y / 3];
}
bool dfs(int cnt){
if(!cnt) return true;
//1找出可选方案数最小的格子
int maxv = 10;
int x, y;
for(int i = 0; i < N; i++)
for(int j = 0; j < N; j++)
if(str[i * 9 + j] == '.'){
int t = ones[get(i, j)];
if(t < maxv){
maxv = t;
x = i;
y = j;
}
}
for(int i = get(x, y); i; i -= lowbit(i)){
int t = map[lowbit(i)];
//修改状态
row[x] -= 1 << t;
col[y] -= 1 << t;
cell[x / 3][y / 3] -= 1 << t;
str[x * 9 + y] = '1' + t;
if(dfs(cnt - 1)) return true;
//回溯
row[x] += 1 << t;
col[y] += 1 << t;
cell[x / 3][y / 3] += 1 << t;
str[x * 9 + y] = '.';
}
return false;
}
int main(){
for(int i = 0; i < N; i++){
map[1 << i] = i;
}
for(int i = 0; i < 1 << N; i++){
int s = 0;
for(int j = i; j ; j -= lowbit(j)) s++;
ones[i] = s;
}
while(cin >> str, str[0] != 'e'){
init();
int cnt = 0;
for(int i = 0, k = 0; i < N; i++){
for(int j = 0; j < N; j++, k++){
if(str[k] != '.'){
int t = str[k] - '1';
row[i] -= 1 << t;
col[j] -= 1 << t;
cell[i / 3][j / 3] -= 1 << t;
}
else cnt++;
}
}
dfs(cnt);
cout << str << endl;
}
return 0;
}