(算法)数独 笼

在这里插入图片描述
已知空的 9 x 9 数独,
尝试将棋盘全部填满,填满后需要满足下述条件:

  1. 每行数字不能重复(一共9个位置,分别需要填入1-9,共9个数字)
  2. 每列数字不能重复(一共9个位置,分别需要填入1-9,共9个数字)
  3. 每个宫数字不能重复(每个33的子棋盘中,也就是上图中的粗实线,将99的棋盘切割成了9个3*3的子棋盘,每个子棋盘中,一共9个位置,分别需要填入1-9,共9个数字)
  4. 虚线圈起来区域(命名为:笼)里面的数字求和,需要等于笼左上方的数字小标(例如棋盘左上角的笼对应数字是11,则当前笼的两个空格对应的数字求和需要等于11。同一个笼内数字可重复)

对于此问题,已提供如下两个方法,可直接使用:
对于棋盘任意位置(i, j),得到当前所在笼的所需的数字和
int getLongTarget(i, j)
举例:假如棋盘左上角坐标为(0, 0),则getLongTarget(0, 0)返回11
对于棋盘任意位置(i, j),得到当前所在笼已填入数字的加和
int getLongCurrentSum(i, j)
举例:假如棋盘左上角坐标为(0, 0),第一行第二个空格的坐标为(0,1),且目前(0, 0)位置已填入3。则此时getLongCurrentSum(0, 1)返回3


分析:

根据数独中笼的位置,可以轻易算出
一排、一列、一宫 之和都是45
3排2列为6 ,(上2排所有笼之和 - 2 x 45)
3排7列为8 ,(右2列所有笼之和 - 2 x 45)
3排4列为7 ,(第5宫所有笼之和 - 1 x 45)
4排4列为3 。(根据4排4列所在笼 - 3排4列)
在这里插入图片描述

算到此处,其他的值就需要根据其他笼的值,进行排除了。
排除也是先看小值的笼,先看格子少的笼。

  • 值小,意味着选择范围少。如笼的值为3的话,那么这两个格子只能为1或者2。
    在这里插入图片描述

  • 格子少,意味着更好排除。如笼的值为15的话,那么格子里的值不能小于6(因为如果小于6,另一个格子值就大于9了)
    在这里插入图片描述

按照这个思路,在根据数独的特性就能得出结论了。
在这里插入图片描述

但是but,毕竟是做开发的,毕竟也是个程序员,运用机器来运算才是上策。
对于数独来说,最好的办法也是最笨的办法,那便是回溯。说白了,一个个去试,先把第一个格放1,然后下一个符不符合,符合在看下一个。。。不符合改为2。这样一个个试,如果试到9了,还不合适,那就无解了。

在写代码前,先吐槽两句:
因为当前数独引入了笼的概念,所以还需要考虑笼的限制。
可以说是限制,也说是我不太理解这个笼的定义,这个笼到底什么是变动的,笼的位置?还是笼的数字和?

  • 如果都不能变,那代码写出来就不可变了呀,扩展性太差了。代码写出来就能解决当前笼的当前值。
  • 如果笼的位置可变,那回溯的时候判断值合不合适的时候的跟谁相加?
  • 如果笼的和可变,emmmmm,好像可以。

行了,上代码吧。(辛辛苦苦把提供的两个方法给写出来了,直呼好家伙~)

package com.chuai.site;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

public class ShuDu {
    private int[][] arr ;

    public ShuDu(){}
    public ShuDu(int[][] arr){
        this.arr = arr;
    }
    public int[][] getArr(){
        return arr;
    }
    private HashSet[] rowSet = new HashSet[9];
    private HashSet[] colSet = new HashSet[9];
    private HashSet[] boxSet = new HashSet[9];
    {
        for(int i = 0;i<9;i++){
            rowSet[i] = new HashSet();
            colSet[i] = new HashSet();
            boxSet[i] = new HashSet();
        }
    }

    public static void main(String[] args) {
        ShuDu shudu = new ShuDu(new int[9][9]);

        long start = System.currentTimeMillis();
        System.out.println("开始时间:"+ start);

        boolean handle = shudu.handle(0);

        long end = System.currentTimeMillis();
        System.out.println("结束时间"+ end);
        System.out.println("时间差:"+ (end-start) + "毫秒");

        System.out.println("有解:" + handle);
        System.out.println("=====================");

        int[][] arr = shudu.getArr();
        Arrays.stream(arr).forEach(a -> {
            Arrays.stream(a).forEach( b -> {
                System.out.print(b + " \t");
            });
            System.out.println();
        });

    }


    public boolean handle(int index){
        // 9*9 数独 最大索引 80
        if (index > 80) { return true; }

        // 根据索引计算坐标
        int row = index/9;
        int col = index%9;

        // 如果当前位置为空,则获取当前位置合法值
        if(arr[row][col] == 0){
            // 获取所有合法值的集合,因为笼的存在,很多位置不一定 1-9
            List<Integer> legalValues = getLegalValues(row, col);
                for (int value : legalValues) {
                    //赋值 记录
                    rowSet[row].add(value);
                    colSet[col].add(value);
                    boxSet[getBoxIndex(row,col)].add(value);
                    arr[row][col] = value;

                    // 如果之后的值都合法,则当前值正确,否则恢复为0,继续遍历
                    if(handle(index+1)){
                        return true;
                    }else {
                        rowSet[row].remove(value);
                        colSet[col].remove(value);
                        boxSet[getBoxIndex(row,col)].remove(value);
                        arr[row][col] = 0;
                    }
                }
                return false;
        }else {     // 如果当前位置有值,则处理下一个位置
            return handle(index+1);
        }
    }

    /**
     *  获取该位置的所有合法值
     * @param row
     * @param col
     * @return
     */
    private List<Integer> getLegalValues(int row, int col){
        List<Integer> resultList = new ArrayList();
        int max = getMax(row, col);
        for(int i = 1;i<= max; i++){
            if( isNoRepeat(row,col,i)){
                resultList.add(i);
            }
        }
        return resultList;
    }

    /**
     *  根据行、列 获取宫的索引
     * @param row
     * @param col
     * @return
     */
    private int getBoxIndex(int row, int col){
        return (row/3)*3 + (col/3);
    }

    /**
     *  判断值是否与 行、列、宫 重复
     * @param row
     * @param col
     * @param value
     * @return true 值合法,不重复
     *         false 错误值,重复
     */
    private boolean isNoRepeat(int row, int col, int value) {
        if(row < 0 || row >= 9 || col < 0 || col >= 9 || value < 1 || value > 9 ){
//            throw new RuntimeException();
            return false;
        }
        //行
        if(rowSet[row].contains(value)){
            return false;
        }
        // 列
        if(colSet[col].contains(value)){
            return false;
        }
        // 宫
        if(boxSet[getBoxIndex(row, col)].contains(value)){
            return false;
        }

        // 合法值 需要符合笼的和
        // 若能确定属于哪个笼,还可以筛选另一个加数大于10的值。
        // 如一个笼2个元素,和为15 ,则 不可能为 1、2、3、4、5 否则另一个数将大于9
        if((getLongTarget(row,col) - getLongCurrentSum(row,col)) < value){
            return false;
        }
        return true;
    }

    /**
     *  获取当前位置的最大值,根据 笼 或者 9
     *  若笼的数字不重复,则可以确定笼中数字的个数 或者 笼的位置。
     *    则 可以从小笼到大笼开始确定,遍历次数更少
     * @param row
     * @param col
     * @return
     */
    private int getMax(int row, int col){
        int longTarget = getLongTarget(row, col);
        int longCurrentSum = getLongCurrentSum(row, col);
        int current = longTarget - longCurrentSum;
        return (9 >= current) ? current : 9;
    }

//    公有方法区。。。
}

好了,核心代码到这里就已经结束了,用的最笨的方式解决了这个数独,还有其他方法可以评论区讨论呀~

下面的这个方法,是题干中提供的公有方法,如果有想跑跑代码啥的可以复制到类里进行测试,不用写测试数据了。嚯嚯,不用谢,我叫磊疯,叫雷锋也行


    /**
     *  已提供方法 可直接使用
     * 获取笼所需数字和
     * @param i
     * @param j
     * @return
     */
    private int getLongTarget(int i, int j){
        if(i == 0){
            if(j == 0 || j == 1){
                return 11;
            }else if(j == 2 || j == 3){
                return 23;
            }else if(j == 4){
                return 12;
            }else if(j == 5 || j == 6){
                return 11;
            }else if(j == 7 || j == 8){
                return 14;
            }
        }else if(i == 1){
            if(j == 0 || j == 1){
                return 12;
            }else if(j == 2 || j == 3){
                return 23;
            }else if(j == 4 || j == 5){
                return 12;
            }else if(j == 6){
                return 11;
            }else if(j == 7 || j == 8){
                return 13;
            }
        }else if(i == 2){
            if(j == 0 ){
                return 6;
            }else if(j == 1){
                return 12;
            }else if(j == 2){
                return 3;
            }else if(j == 3){
                return 10;
            }else if(j == 4 || j == 5){
                return 13;
            }else if(j == 6 || j == 7){
                return 15;
            }else if(j == 8){
                return 11;
            }
        }else if(i == 3){
            if(j == 0 ){
                return 6;
            }else if(j == 1){
                return 13;
            }else if(j == 2){
                return 3;
            }else if(j == 3){
                return 10;
            }else if(j == 4 || j == 5){
                return 15;
            }else if(j == 6 ){
                return 8;
            }else if(j == 7){
                return 15;
            }else if(j == 8){
                return 11;
            }
        }else if(i == 4){
            if(j == 0 ){
                return 12;
            }else if(j == 1){
                return 13;
            }else if(j == 2){
                return 10;
            }else if(j == 3 || j == 4){
                return 9;
            }else if( j == 5){
                return 3;
            }else if(j == 6 ){
                return 8;
            }else if(j == 7){
                return 8;
            }else if(j == 8){
                return 11;
            }
        }else if(i == 5){
            if(j == 0 ){
                return 12;
            }else if(j == 1){
                return 14;
            }else if(j == 2){
                return 10;
            }else if(j == 3 || j == 4){
                return 15;
            }else if( j == 5){
                return 3;
            }else if(j == 6 ){
                return 12;
            }else if(j == 7){
                return 8;
            }else if(j == 8){
                return 11;
            }
        }else if(i == 6){
            if(j == 0 || j == 1){
                return 14;
            }else if(j == 2){
                return 23;
            }else if(j == 3 || j == 4){
                return 11;
            }else if( j == 5){
                return 14;
            }else if(j == 6 ){
                return 12;
            }else if(j == 7){
                return 10;
            }else if(j == 8){
                return 5;
            }
        }else if(i == 7){
            if(j == 0 || j == 1){
                return 9;
            }else if(j == 2 || j == 3){
                return 23;
            }else if(j == 4){
                return 6;
            }else if( j == 5){
                return 14;
            }else if(j == 6 ){
                return 19;
            }else if(j == 7){
                return 10;
            }else if(j == 8){
                return 5;
            }
        }else if(i == 8){
            if(j == 0 || j == 1){
                return 13;
            }else if(j == 2 || j == 3){
                return 23;
            }else if(j == 4){
                return 6;
            }else if( j == 5 || j == 6){
                return 19;
            }else if(j == 7 || j == 8){
                return 11;
            }
        }

        return -1 ;
    }

    /**
     *  已提供方法 可直接使用
     * 获取笼已填入数字和
     * @param i
     * @param j
     * @return
     */
    private int getLongCurrentSum(int i, int j){
        if(i == 0 && (j == 0 || j == 1)) {
            return arr[0][0] + arr[0][1];
        }
        if((i == 1 && (j == 0 || j == 1)) || (i == 2 && j == 1)) {
            return arr[1][0] + arr[1][1] + arr[2][1];
        }
        if((i == 2 || i == 3 )&& j == 0) {
            return arr[2][0] + arr[3][0];
        }
        if((i == 4 || i == 5 )&& j == 0) {
            return arr[4][0] + arr[5][0];
        }
        if((i == 3 || i == 4 )&& j == 1) {
            return  arr[3][1] + arr[4][1];
        }
        if((i == 5 && j == 1) || (i == 6 && (j == 0 || j == 1))) {
            return  arr[5][1] + arr[6][0] + arr[6][1];
        }
        if(i == 7 && (j == 0 || j == 1)) {
            return arr[7][0] + arr[7][1];
        }
        if(i == 8 && (j == 0 || j == 1)) {
            return  arr[8][0] + arr[8][1];
        }


        if( (i == 0 && (j == 2 || j == 3)) || (i == 1 && (j == 2 || j == 3)) ){
            return arr[0][2]+arr[0][3]+arr[1][2]+arr[1][3];
        }
        if((i == 2 || i == 3) && j == 2){
            return arr[2][2]+arr[3][2];
        }
        if((i == 2 || i == 3) && j == 3){
            return arr[2][3]+arr[3][3];
        }
        if((i == 4 || i == 5) && j == 2){
            return arr[4][2]+arr[5][2];
        }
        if(i == 4 && (j == 3 || j == 4)){
            return arr[4][3]+arr[4][4];
        }
        if(i == 5 && (j == 3 || j == 4)){
            return arr[5][3]+arr[5][4];
        }
        if(i == 6 && (j == 3 || j == 4)){
            return arr[6][3]+arr[6][4];
        }
        if((i == 6 && j == 2) || ((i == 7 || i == 8) && (j == 2 || j == 3)) ){
            return arr[6][2]+arr[7][2] +arr[7][3] +arr[8][2]+arr[8][3];
        }


        if( ((i == 0 || i == 1) && j == 4) || (i == 1 && j == 5) ){
            return arr[0][4]+arr[1][4] +arr[1][5] ;
        }
        if(i == 2 && (j == 4 || j == 5)){
            return arr[2][4]+arr[2][5];
        }
        if(i == 3 && (j == 4 || j == 5)){
            return arr[3][4]+arr[3][5];
        }
        if((i == 4 || i == 5) && j == 5){
            return arr[4][5]+arr[5][5];
        }
        if((i == 6 || i == 7) && j == 5){
            return arr[6][5]+arr[7][5];
        }
        if((i == 7 || i == 8) && j == 4){
            return arr[7][4]+arr[8][4];
        }

        if((i == 0 && (j == 5 || j == 6)) || (i == 1 && j == 6)) {
            return arr[0][5] + arr[0][6] + arr[1][6];
        }
        if(i == 0 && (j == 7 || j == 8)) {
            return arr[0][7] + arr[0][8];
        }
         if(i == 1 && (j == 7 || j == 8)) {
            return arr[1][7] + arr[1][8];
        }
        if((i == 2 && (j == 6 || j == 7)) || (i == 3 && j == 7)) {
            return arr[2][6] + arr[2][7] + arr[3][7];
        }
        if((i == 2 || i == 3) && j == 8){
            return arr[2][8]+arr[3][8];
        }
        if((i == 4 || i == 5) && j == 8){
            return arr[4][8]+arr[5][8];
        }
        if((i == 3 || i == 4) && j == 6){
            return arr[3][6]+arr[4][6];
        }
        if((i == 5 || i == 6) && j == 6){
            return arr[5][6]+arr[6][6];
        }
        if((i == 4 || i == 5) && j == 7){
            return arr[4][7]+arr[5][7];
        }
        if((i == 6 || i == 7) && j == 7){
            return arr[6][7]+arr[7][7];
        }
        if((i == 6 || i == 7) && j == 8){
            return arr[6][8]+arr[7][8];
        }
        if(i == 8 && (j == 7 || j == 8)) {
            return arr[8][7] + arr[8][8];
        }
        if((i == 7 && j == 6) || (i == 8 && (j == 5 || j == 6))) {
            return  arr[7][6] + arr[8][5] + arr[8][6];
        }
        return -1;
    }

这回所有代码都结束了,真没有了,别翻了。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值