题意:
一个数独问题,类似的还有POJ2676 = POJ 2918 < POJ3074 < POJ3076,按难度排序。
解法:
大部分的题解都是用舞蹈链写的,但是实在不想学这个不太常用的算法,就一直尝试剪枝。成功剪了n小时......
两个剪枝操作:
1.每次寻找可以填的数字最少的那个格子
2.每次更新的时候,顺便寻找是否有一行,或者有一列只能填一个数,然后进行处理
解法:
思路虽然不难,但是代码不太好设计,这题我从早上一直写到下午,才研究清楚如何剪枝,下面讲讲我的想法
先用num[i][j]来记录(i,j)这个点可以填几个数字
再用num_t[i][j][k]来记录(i,j)这个点是否可以填k这个数字,如果等于1,则可以填,如果不为1则不能填
建立一个函数find(),用来寻找num[i][j]最小的点,从可填数字最少的点入手,实现第一个剪枝操作
然后将find找到的(x,y)这个点传入dfs,进行搜索
对于(x,y)这个点,1-9枚举可以填的数字,如果数字k可以填,那就先将这个数字填上,然后update(),再进行dfs递归操作
update()有两个作用:
1.因为填上了某个数字,因此需要改变所有数字的num[i][j],求num[i][j]的时候顺便求出sum1、sum2、sum3
· sum1[i][j]表示第i行可以填数字j
· sum2[i][j]表示第i列可以填数字j
· sum3[i][j]表示第i个九宫格可以填数字j
2.由于求出了sum1、sum2、sum3,就可以对sum123进行遍历,如果找到某一行的某一个点只能填一次,就将这个点的num改成1,num_t的其他值都改成0
这样的话就可以实现成功的剪枝。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define rep(i,a,b) for(int i = a;i <= b;i++)
#define per(i,a,b) for(int i = b;i >= a;i--)
using namespace std;
int row[10][10],col[10][10],grid[10][10];
int map[11][11];
//int sum1[11],sum2[11],sum3[11]; //sum1:行 sum2:列 sum3:九宫格
int num[11][11]; //num[i][j]:(i,j)处可以填几个数字
int num_t[11][11][11]; //num_t[i][j][k] == 1 i,j处可以填k
int dfs(int x,int y);
struct Po{
int x,y;
}e[100];
int zero,tot;
void update()
{
int sum1[11][11],sum2[11][11],sum3[11][11];
memset(num,0,sizeof num);
memset(sum1,0,sizeof sum1); //sum1[i][j]表示第i行可以填数字j
memset(sum2,0,sizeof sum2); //sum2[i][j]表示第i列可以填数字j
memset(sum3,0,sizeof sum3); //sum3[i][j]表示第i个方块可以填数字j
rep(hm,1,tot) //总共有tot个空白处
{
int i = e[hm].x;
int j = e[hm].y;
if(!map[i][j]) //如果(i,j)这个点是空格
{
int h = 3*((i-1)/3)+(j-1)/3+1; //属于哪个九宫格
per(k,1,9) //看k能不能填入这个空格
{
num_t[i][j][k] = 0;
if(!row[i][k] && !col[j][k] && !grid[h][k])
{
num[i][j]++;
num_t[i][j][k] = 1;
sum1[i][k]++;
sum2[j][k]++;
sum3[h][k]++;
}
}
}
}
rep(i,1,9) //i行、i列
{
rep(j,1,9) //填j的数
{
if(sum1[i][j] == 1)
{
rep(k,1,9) //k列
{
if(num_t[i][k][j] == 1)
{
num[i][k] = 1; //(i,k)处只能填一个数字
rep(hm,1,9)
num_t[i][k][hm] = 0;
num_t[i][k][j] = 1; //只能填j这个数字
}
}
}
}
rep(j,1,9)
{
if(sum2[i][j] == 1) //i列填j这个数有几种填法
{
rep(k,1,9) //k行
{
if(num_t[k][i][j] == 1)
{
num[k][i] = 1;
rep(hm,1,9)
num_t[k][i][hm] = 0;
num_t[k][i][j] = 1;
}
}
}
}
}
}
Po find()
{
Po tmp;
tmp.x = 0,tmp.y = 0;
Po fa;
fa.x = -10,fa.y = -10;
if(zero == 0) return tmp;
int maxx = 100;
rep(hm,1,tot)
{
int i = e[hm].x;
int j = e[hm].y;
if(!map[i][j])
{
if(num[i][j] == 0) return fa;
if(num[i][j] < maxx && num[i][j] != 0)
{
tmp.x = i;
tmp.y = j;
maxx = num[i][j];
}
}
}
// cout<<maxx<<endl;
return tmp;
}
int dfs(int x,int y)
{
if(x == -10 && y == -10) return 0;
if(zero == 0) return 1;
if(x < 1 || x > 9 || y < 1 || y > 9) return 1;
int k = 3*((x-1)/3)+(y-1)/3+1;
rep(i,1,9)
{
if(num_t[x][y][i] == 0) continue;
if(!row[x][i] && !col[y][i] && !grid[k][i])
{
zero--;
map[x][y] = i;
row[x][i] = 1;
col[y][i] = 1;
grid[k][i] = 1;
update();
int jud;
Po tt = find();
jud = dfs(tt.x,tt.y);
if(jud){
return jud;
}
else
{
zero++;
map[x][y] = 0;
row[x][i] = 0;
col[y][i] = 0;
grid[k][i] = 0;
update();
}
}
}
return 0;
}
int main()
{
char cc[100];
while(scanf("%s",cc) && cc[0] != 'e')
{
rep(i,0,80){
int x = i/9+1;
int y;
if(i <= 8) y = i+1;
else y = (i+1)-(x-1)*9;
if(cc[i] == '.') map[x][y] = 0;
else map[x][y] = cc[i]-'0';
}
memset(row,0,sizeof row);
memset(col,0,sizeof col);
memset(grid,0,sizeof grid);
memset(num_t,0,sizeof num_t);
zero = 0; tot = 0;
rep(i,1,9){
rep(j,1,9){
int k = 3*((i-1)/3)+(j-1)/3+1;
if(map[i][j] != 0){
row[i][map[i][j]]++;
col[j][map[i][j]]++;
grid[k][map[i][j]]++;
}
else{
zero++;
e[++tot].x = i;
e[tot].y = j;
}
}
}
update();
Po tt = find();
dfs(tt.x,tt.y);
rep(i,1,9)
rep(j,1,9)
printf("%d",map[i][j]);
printf("\n");
/* rep(i,1,9)
{
rep(j,1,8) printf("%d",map[i][j]);
printf("%d\n",map[i][9]);
}*/
}
return 0;
}