废话少说,先上程序(直接将代码copy到一个All的java文件中,删除package即可运行,答案将显示到控制台):
Tip:sourceInput为数独的初始状态,可以自行更改
package mySuduku;
import java.util.ArrayList;
import java.util.List;
public class All {
public static void main(String args[]){
/*
* 初始数据(map)
*/
int sourceInput[][]={ {0,0,7,0,5,0,0,0,0},
{6,0,0,2,0,1,7,0,0},
{0,1,2,0,0,0,4,0,8},
{3,2,4,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,6,0,8,0,0},
{0,0,0,5,0,0,3,0,0},
{7,0,0,8,0,0,0,4,0},
{8,4,0,0,0,3,5,7,0}};
/* 用来分析的map
* 后期可以填补,以便简化map
*/
int inputMap[][]=new int[9][9];
/*
* 分析使用的map(使map不能再优化)
* analyInputMap[x][y][z]中xy对应inputMap[x][y],[z]中1-9代表1-9的可能性,
* 10代表这个数是否在原map中已经存在
*/
int analyInputMap[][][]=new int[9][9][10];
//与分析的map做比较用的
int tempAnalyMap[][][]=new int[9][9][10];
//比较两个map(这个看是否是优化为最终结果)
boolean compareInputToTemp=true;
//将sourceInput塞入inputMap中
setSameMap(sourceInput,inputMap,9,9);
/*
* 去除inputMap中有的值
* 即置analyInputMap[x][y][z]中的z位为该数(map[x][y])不可用
*/
deleteFromMap(sourceInput,analyInputMap);
/*
* 两步删除不可能的结果
* 两步分别指: 1对所有剩下的空白格,求出其可能的数值,
* 2当其可能数值仅有一种时,更新inputMap填上这一种,然后将analyInputMap进行调整
* 当输入和输出的值一样的时候认为无法再进一步简化
*/
while(compareInputToTemp){
//赋值inputMap到tempMap
setSameMap(analyInputMap,tempAnalyMap,9,9,10);
//检查是否可以进一步简化
arrange(inputMap,analyInputMap);
//判断上步简化是否确实发生了变化
compareInputToTemp=compareAB(analyInputMap,tempAnalyMap,9,9,10);
printMap(inputMap,9,9);
printMap(analyInputMap,9,9,9);
}
/*
* while后结果可视为初步最优解,此时考虑遍历所有剩下的可能情况,用countRest记录剩余空白格
* 对每个空白格需记录这么几个项:1坐标(即x,y),2可能的数字
* 这里采用一个类(theRest)记录
* 类中的X,Y记录坐标,avaiable记录可能的数值
*
* 最后,用List<theRest> theRestNumber装下所有theRest
*/
//剩下还有多少空没有填
int countRest=0;
//每个填的数字需要用个数据结构(类theRest)
List<theRest> theRestNumber=new ArrayList<theRest>();
//一个暂时的temp指针,指向一个theRest类,这里先设置了,减小开销
theRest temp;
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(inputMap[i][j]==0){
countRest++;
temp=new theRest(i,j);
for(int k=0;k<9;k++){
if(analyInputMap[i][j][k]==0)
temp.avaiable.add(k+1);
}
theRestNumber.add(temp);
}
//试试一个数字
tryANumber(inputMap,countRest,countRest,theRestNumber);
}
/*
* 试试一个number
* 整体思路如下,输入参数为map(最终优化后结果),计数,整体theRestNumber的length,theRestNumber
* 出口就是填满所有数字(由于是先检查,再填数字,所以最后不用检查)
*/
public static void tryANumber(int[][] map,int nowRest,int allRestNum,List<theRest> theRestNumber){
/* printMap(map,9,9);*/
if(nowRest==0){
printMap(map,9,9);
}
else if(allRestNum-nowRest>=0&&allRestNum!=0){
theRest temp=theRestNumber.get(allRestNum-nowRest);
boolean flag=false;
int i;
for(i=0;i<temp.avaiable.size();i++){
flag=check(map,temp,temp.avaiable.get(i));
//如果值是可用的,则将值放入,并进行下一次的迭代
if(flag)
{
map[temp.x][temp.y]=temp.avaiable.get(i);
tryANumber(map,nowRest-1,allRestNum,theRestNumber);
//当try遍历完成后,将尝试的值置空
map[temp.x][temp.y]=0;
}
}
}
}
//检查放入一个number以后是否可行
public static boolean check(int[][] map,theRest temp,int tryNumber){
boolean flag=true;
//检查横行和竖行是否有数字已经要尝试的数字了
for(int i=0;i<9;i++)
if(map[temp.x][i]==tryNumber||map[i][temp.y]==tryNumber)
flag=false;
//排除坐标对应3*3格中有没有尝试的数字
int tempx=temp.x/3;
int tempy=temp.y/3;
for (int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(map[tempx*3+i][tempy*3+j]==tryNumber)
flag=false;
return flag;
}
//从分析的map中删除map中有的元素(即置标签位(z=9)为空)
public static void deleteFromMap(int[][] source,int[][][] target){
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(source[i][j]!=0)
target[i][j][9]=1;
};
/*
* 安置函数,入参是inputMap,和analyInputMap
* 当a(即inputMap)每出现一个数字时,在对应的b(即analyInputMap)中进行相应动作(controlB)
* 该动作排除a出现后限制b中的可能性(即a中空白处对应的可能数字要相应减少)
* 在动作后,对b经行遍历,发现若a的空白处可填项仅有一个时,对a进行填项,填项后对b的该坐标位置不可填
*/
public static void arrange(int[][] a,int[][][]b){
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(a[i][j]!=0)
controlB(i,j,a[i][j],b);
//是否a的空白出只有一个可填项
int countIsOnly;
//对b经行遍历,发现若a的空白处可填项仅有一个时,对a进行填项,填项后对b的该坐标位置不可填
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(a[i][j]==0&&b[i][j][9]!=1){
countIsOnly=9;
//循环,发现1则减去,当8个1时,说明只有一个可填,填项
for(int k=0;k<9;k++)
countIsOnly-=b[i][j][k];
if(countIsOnly==1){
for(int k=0;k<9;k++)
if(b[i][j][k]==0)
a[i][j]=k+1;
b[i][j][9]=1;
}
}
}
public static void controlB(int x,int y,int value,int[][][] b){
for(int i=0;i<9;i++){
//排除每行第x个字
b[x][i][value-1]=1;
//排除每列第x个字
b[i][y][value-1]=1;
}
//排除坐标对应的3*3格
int tempx=x/3;
int tempy=y/3;
for (int i=0;i<3;i++)
for(int j=0;j<3;j++){
b[tempx*3+i][tempy*3+j][value-1]=1;
}
}
//复制二维数组函数,不多说
public static void setSameMap(int[][]source,int[][]target,int x,int y){
for(int i=0;i<x;i++)
for(int j=0;j<y;j++)
target[i][j]=source[i][j];
}
//复制三位数组函数,不多说
public static void setSameMap(int[][][]source,int[][][]target,int x,int y,int z){
for(int i=0;i<x;i++)
for(int j=0;j<y;j++)
for(int k=0;k<z;k++)
target[i][j][k]=source[i][j][k];
}
//比较二维数组函数,不多说
public static boolean compareAB(int[][]a,int[][] b,int x,int y){
boolean flag=true;
for(int i=0;i<x;i++)
for(int j=0;j<y;j++)
if(b[i][j]!=a[i][j])
flag=false;
return flag;
}
//比较三维数组函数,不多说
public static boolean compareAB(int[][][]a,int[][][] b,int x,int y,int z){
boolean flag=false;
for(int i=0;i<x;i++)
for(int j=0;j<y;j++)
for(int k=0;k<z;k++)
if(b[i][j][k]!=a[i][j][k])
flag=true;
return flag;
}
//打印二维map,不多说
public static void printMap(int[][] map,int x,int y){
for(int i=0;i<x;i++){
System.out.println();
for(int j=0;j<y;j++){
System.out.print(map[i][j]+" ");
}
}
System.out.println();
}
//打印三维map,不多说
public static void printMap(int[][][] map,int x,int y,int z){
for(int i=0;i<x;i++){
System.out.println();
for(int j=0;j<y;j++){
if(map[i][j][9]!=1){
System.out.print("map【"+i+"】:"+"【"+j+"】:");
for(int k=0;k<9;k++){
if(map[i][j][k]!=1)
System.out.print(k+1);
System.out.print(" ");
}
}
}
}
System.out.println();
}
}
/*
* 一个简单类,用装剩余空白格的信息
* x,y记录空白处的坐标
* avaiable记录可填充的数字
*/
class theRest{
public int x;
public int y;
public List<Integer> avaiable=new ArrayList<Integer>();
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public theRest(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
程序总体思路:
1输入状态
2解决
2.1 将总体状态放入一个sourceInput[9][9]
2.2 优化sourceInput,用inputMap[9][9]存放最后优化结果
2.2.1 优化,以下步骤
- 当map中每出现一个数,在其余的横竖和对应的3*3格子内不允许出现该数字,注意剩下的空白格子中可填的数字,
- 当空白格子中可填数字仅剩一个时,填入该数字,
- 重复上两步,直到map不再发生变动