数独

来源:互联网 发布:java gzip 压缩 编辑:程序博客网 时间:2024/05/02 02:35

废话少说,先上程序(直接将代码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不再发生变动

2.3 对于优化的结果,可记num个空格,每个空格的可能填的数字可能个数记为an,则此时全遍历的情况为a1*a2*a3*…*an(n=num),

2.4 这里采用思想是深度遍历,当遇到结果就打印,然后继续下一步,直到遍历完(以防止多解情况)

3输出答案

Ps:这里仅是一次尝试,还有好多不妥之处,还望见谅~

0 0