数独
9x9的格子均分为3x3块,其中一些格子填有1~9之间的数字。请把空格填满,使得
每行,每列和每个块中,1~9每个数字只出现一次。
直接dfs, 可能要检查9^81个。
P1. 从左上角第一个空白开始,在搜索过程中,填每行,每列,每块没有出现过的数字。如果没有,就不要继续向下搜了。
P2. 如果某一行只有一个空白,或者某个格子可填的数只有一个,优先处理。
如果某个空白的候选数字比较少,优先处理;因而一开始不必从左上角开始。
P1 作为基本的参考实现,否则程序都停不下来。搜索路径:从左上角到右下角先行后列填空白。
P1(x, y)表示以(x, y)为左上角开始找空格。
P2 作为优化实现,搜索路径不再是顺序的,到下一个空格的位置是动态计算的。
但是下面的P2()实现花的时间是P1()的4倍。一个改进办法是维护一个全局的每个空格的候选集。
因为当前的输入测试,P1()检查的节点数是291406, P2()是298451, 即使再改时间估计不会小于P1。
9x9的格子均分为3x3块,其中一些格子填有1~9之间的数字。请把空格填满,使得
每行,每列和每个块中,1~9每个数字只出现一次。
直接dfs, 可能要检查9^81个。
P1. 从左上角第一个空白开始,在搜索过程中,填每行,每列,每块没有出现过的数字。如果没有,就不要继续向下搜了。
P2. 如果某一行只有一个空白,或者某个格子可填的数只有一个,优先处理。
如果某个空白的候选数字比较少,优先处理;因而一开始不必从左上角开始。
P1 作为基本的参考实现,否则程序都停不下来。搜索路径:从左上角到右下角先行后列填空白。
P1(x, y)表示以(x, y)为左上角开始找空格。
P2 作为优化实现,搜索路径不再是顺序的,到下一个空格的位置是动态计算的。
但是下面的P2()实现花的时间是P1()的4倍。一个改进办法是维护一个全局的每个空格的候选集。
因为当前的输入测试,P1()检查的节点数是291406, P2()是298451, 即使再改时间估计不会小于P1。
就不改了。可能对于 poj 3076 16x16 有用些。
语言注记: System.out.printf() 打印实在是影响运行时间,测试速度时应当把不必要的打印去掉。
代码:
import java.util.Scanner;
import java.util.Arrays;
public class Sudo{
static final int N = 9, M = 3;
static char[][] grid = null;
static int cnt = 0;
static boolean P2()
{
int i;
int[] pos = new int[2];
++cnt;
if(!hasBlank()){
dump();
return true;
}
char[] cdt = getLeastCandidate(pos);
if(0 == cdt.length)return false;
for(i = 0; i < cdt.length; ++i){
grid[pos[0]][pos[1]] = cdt[i];
if( P2()) return true;
grid[pos[0]][pos[1]] = '0';
}
return false;
}
static boolean hasBlank()
{
int i, j;
for(i = 0; i < N; ++i){
for(j = 0; j < N; ++j){
if(grid[i][j] == '0')return true;
}
}
return false;
}
static char[] getLeastCandidate(int[] pos)
{
int i, j, k, ii = -1, jj = -1;
int min = N+1, tmp;
char[] cdt = new char[N];
char[] bak = new char[N];
for(i = 0; i < N; ++i){
for(j = 0; j < N; ++j){
if(grid[i][j] == '0'){
tmp = getCandidates(i, j, cdt);
if(tmp < min){
min = tmp;
for(k = 0; k < min; ++k)bak[k] = cdt[k];
ii = i; jj = j;
}
}
}
}
pos[0] = ii; pos[1] = jj;
bak = Arrays.copyOf(bak, min);
return bak;
}
static int getCandidates(int x, int y, char[] cdt)
{
int i, j, k, l;
boolean[] mask = new boolean[N];
for(i = 0; i < N; ++i)if(grid[x][i] != '0')mask[grid[x][i] - '1'] = true;
for(i = 0; i < N; ++i)if(grid[i][y] != '0')mask[grid[i][y] - '1'] = true;
k = (x/3)*3; l = (y/3)*3;
for(i = 0; i < M; ++i)for(j = 0; j < M; ++j)
if(grid[k+i][l+j] != '0')mask[ grid[k+i][l+j] - '1' ] = true;
for(k = 0, i = 0; i < N; ++i)if(!mask[i]){
cdt[k++] = (char)('1' + i);
}
return k;
}
static boolean canFill(int x, int y, char c)
{
int i, j, k, l;
for(j = 0; j < N; ++j)if(grid[x][j] == c)return false;
for(i = 0; i < N; ++i)if(grid[i][y] == c)return false;
k = (x/3)*3; l = (y/3)*3;
for(i = 0; i < M; ++i)for(j = 0; j < M; ++j)
if(grid[k+i][l+j] == c)return false;
return true;
}
static boolean P1(int x, int y)
{
int ii, jj, i = -1, j = -1;
int found = 0;
char c;
++cnt;
for(i = x, j = y; i < N && j < N; ){
if('0' == grid[i][j]){
found = 1;
break;
}else{
j = j + 1;
if(j == N){i = i + 1; j = 0;}
}
}
//if(i >= 7)System.out.printf("handle %d %d ij %d %d %n", x, y, i, j);
if(0 == found){
dump();
return true;
}
for(c = '1'; c <= '9'; ++c){
if(canFill(i, j, c)){
grid[i][j] = c;
if(j < N-1){ii = i; jj = j +1;}else{ii = i + 1; jj = 0;}
if(P1(ii, jj))return true;
grid[i][j] = '0'; /*还原,尝试下一个数字*/
}
}
return false;
}
static void dump()
{
int i, j;
System.out.printf("grid:%n");
for(i = 0; i < N; ++i){
for(j = 0; j < N; ++j){
System.out.printf("%c", grid[i][j]);
}
System.out.printf("%n");
}
}
public static void main(String[] arg) throws Exception
{
Scanner in = new Scanner(System.in);
grid = new char[N][N];
int i, j;
for(i = 0; i < N; ++i){
String line = in.nextLine();
for(j = 0; j < N; ++j){
grid[i][j] = line.charAt(j);
}
}
in.close();
//P1(0, 0);
P2();
System.out.printf("nodes %d%n", cnt);
}
}
/*
$ cat in.txt
000000520
080400000
030009000
501000600
200700000
000300000
600010000
000000704
000000030
$ time cat in.txt | java Sudo
grid:
416837529
982465371
735129468
571298643
293746185
864351297
647913852
359682714
128574936
real 0m0.386s
user 0m0.471s
sys 0m0.043s
*/