数独

废话少说,先上程序(直接将代码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:这里仅是一次尝试,还有好多不妥之处,还望见谅~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值