Java题解 | HJ44 #Sudoku#

Sudoku

https://www.nowcoder.com/practice/78a1a4ebe8a34c93aac006c44f6bf8a1

描述

问题描述:数独(Sudoku)是一款大众喜爱的数字逻辑游戏。玩家需要根据9X9盘面上的已知数字,推算出所有剩余空格的数字,并且满足每一行、每一列、每一个粗线宫内的数字均含1-9,并且不重复。

输入:包含已知数字的9X9盘面数组(空缺位以数字0表示)

输出:完整的9X9盘面数组

输入描述:包含已知数字的9X9盘面数组(空缺位以数字0表示)

输出描述:完整的9X9盘面数组

示例1

输入

0 9 2 4 8 1 7 6 3
4 1 3 7 6 2 9 8 5
8 6 7 3 5 9 4 1 2
6 2 4 1 9 5 3 7 8
7 5 9 8 4 3 1 2 6
1 3 8 6 2 7 5 9 4
2 7 1 5 3 8 6 4 9
3 8 6 9 1 4 2 5 7
0 4 5 2 7 6 8 3 1

输出
5 9 2 4 8 1 7 6 3
4 1 3 7 6 2 9 8 5
8 6 7 3 5 9 4 1 2
6 2 4 1 9 5 3 7 8
7 5 9 8 4 3 1 2 6
1 3 8 6 2 7 5 9 4
2 7 1 5 3 8 6 4 9
3 8 6 9 1 4 2 5 7
9 4 5 2 7 6 8 3 1

解法

  • 由“满足每一行、每一列、每一个粗线宫内的数字均含1-9,并且不重复”的特点,可以推断出行、列、宫能填入剩余空格的数字的候选人列表;
  • 先从第1个空格的候选人列表选出1个候选人,而后从第2个空格的候选人列表选出1个候选人。如果上述候选人能够满足填入条件,则尝试下一个空格;如果不能满足填入条件,从第2个空格的候选人列表尝试选出另外候选人;如果还不能满足填入条件,从第1个空格的候选人列表尝试其他候选人;
  • 怎么查当前格子在哪个九宫格?((x/3)*3, (y/3)*3)即为九宫格左上角的坐标。
/*
 * Copyright (c) waylau.com, 2022\. All rights reserved.
 */

package com.waylau.nowcoder.exam.oj.huawei;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * HJ44 Sudoku.
 * 问题描述:数独(Sudoku)是一款大众喜爱的数字逻辑游戏。玩家需要根据9X9盘面上的已知数字,
 * 推算出所有剩余空格的数字,并且满足每一行、每一列、每一个粗线宫内的数字均含1-9,并且不重复
 * 输入:包含已知数字的9X9盘面数组(空缺位以数字0表示)
 * 输出:完整的9X9盘面数组
 * 输入描述:包含已知数字的9X9盘面数组(空缺位以数字0表示)
 * 输出描述:完整的9X9盘面数组
 *
 * @author Way Lau
 * @since 2022-08-23
 */
public class HJ044Sudoku {
    private static final int N = 9;

    public static void main(String[] args) {
        // 输入
        Scanner in = new Scanner(System.in);

        List zeroGridList = new ArrayList();

        // 输入的数组arr[n][m]的值0表示空格
        int[][] arr = new int[N][N];
        int index = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                int gridValue = in.nextInt();
                arr[i][j] = gridValue;

                if (gridValue == 0) {
                    zeroGridList.add(new Grid(index, i, j));
                    index++;
                }
            }
        }

        // 执行
        Stack gridStack = new Stack();

        // 记录下个格子
        int nextIdex = 0;
        while (nextIdex < zeroGridList.size()) {
            nextIdex = doSudoku(arr, gridStack,
                    zeroGridList.get(nextIdex), false);
        }

        // 输出
        for (int i = 0; i < N; i++) {
            StringBuilder sb = new StringBuilder();

            for (int j = 0; j < N; j++) {
                sb.append(arr[i][j]);
                sb.append(" ");
            }

            System.out.println(sb.toString().trim());
        }

        // 关闭
        in.close();
    }

    private static int doSudoku(int[][] arr,
            Stack gridStack, Grid zeroGrid,
            boolean isPreviousGrid) {
        Queue candidates = zeroGrid.candidates;

        // 如果是从栈里面找出来的格子,说明之前已经查过候选人,无需重新再查
        if (!isPreviousGrid) {
            candidates = selectCandidate(arr, zeroGrid.x,
                    zeroGrid.y);
            zeroGrid.candidates = candidates;
        }

        if (!candidates.isEmpty()) {
            // 从候选里面取一个
            int candidate = candidates.poll();
            arr[zeroGrid.x][zeroGrid.y] = candidate;
            gridStack.add(zeroGrid);

            return zeroGrid.index + 1;
        } else {
            // 找不到候选人,说明前面的空格填的有问题
            // 回退到上一个格子找后续的候选人
            Grid previousGrid = gridStack.pop();
            arr[previousGrid.x][previousGrid.y] = 0;

            return doSudoku(arr, gridStack, previousGrid,
                    true);
        }

    }

    // 查找候选人列表
    private static Queue selectCandidate(
            int[][] arr, int x, int y) {
        Queue candidates = new ArrayBlockingQueue(
                N);

        for (int candidate = 1; candidate <= N; candidate++) {
            // 先探行,再探列,再探九宫格
            for (int j = 0; j < N; j++) {
                if (!contains(arr[x], candidate)
                        && !containsColumn(arr, y,
                                candidate)
                        && !containsGrid(arr, x, y,
                                candidate)) {
                    candidates.add(candidate);

                    break;
                }
            }
        }

        return candidates;
    }

    // 判断是否包含在行里
    private static boolean contains(int[] a, int val) {
        for (int i = 0; i < a.length; i++) {
            if (a[i] == val) {
                return true;
            }
        }
        return false;
    }

    // 判断是否包含在列里
    private static boolean containsColumn(int[][] a,
            int column, int val) {
        for (int i = 0; i < a.length; i++) {
            if (a[i][column] == val) {
                return true;
            }
        }
        return false;
    }

    // 是否包含在九宫格里面
    private static boolean containsGrid(int[][] a, int x,
            int y, int val) {
        // 找到所在九宫格的左上角
        int gridX = (x / 3) * 3;
        int gridY = (y / 3) * 3;

        for (int i = gridX; i < gridX + 3; i++) {
            for (int j = gridY; j < gridY + 3; j++) {
                if (a[i][j] == val) {
                    return true;
                }
            }
        }

        return false;
    }

    private static class Grid {
        public int index;

        public int x;

        public int y;

        public Queue candidates = new ArrayBlockingQueue(
                N);

        public Grid(int index, int x, int y) {
            this.index = index;
            this.x = x;
            this.y = y;
        }
    }
}

参考引用

  • 本系列归档至
  • 《Java 数据结构及算法实战》:
  • 《数据结构和算法基础(Java 语言实现)》(柳伟卫著,北京大学出版社出版):
#华为机考#
全部评论

相关推荐

避坑恶心到我了大家好,今天我想跟大家聊聊我在成都千子成智能科技有限公司(以下简称千子成)的求职经历,希望能给大家一些参考。千子成的母公司是“同创主悦”,主要经营各种产品,比如菜刀、POS机、电话卡等等。听起来是不是有点像地推销售公司?没错,就是那种类型的公司。我当时刚毕业,急需一份临时工作,所以在BOSS上看到了千子成的招聘信息。他们承诺无责底薪5000元,还包住宿,这吸引了我。面试的时候,HR也说了同样的话,感觉挺靠谱的。于是,我满怀期待地等待结果。结果出来后,我通过了面试,第二天就收到了试岗通知。试岗的内容就是地推销售,公司划定一个区域,然后你就得见人就问,问店铺、问路人,一直问到他们有意向为止。如果他们有兴趣,你就得摇同事帮忙推动,促进成交。说说一天的工作安排吧。工作时间是从早上8:30到晚上18:30。早上7点有人叫你起床,收拾后去公司,然后唱歌跳舞(销售公司都这样),7:55早课(类似宣誓),8:05同事间联系销售话术,8:15分享销售技巧,8:30经理训话。9:20左右从公司下市场,公交、地铁、自行车自费。到了市场大概10点左右,开始地推工作。中午吃饭时间大约是12:00,公司附近的路边盖饭面馆店自费AA,吃饭时间大约40分钟左右。吃完饭后继续地推工作,没有所谓的固定中午午休时间。下午6点下班后返回公司,不能直接下班,需要与同事交流话术,经理讲话洗脑。正常情况下9点下班。整个上班的一天中,早上到公司就是站着的,到晚上下班前都是站着。每天步数2万步以上。公司员工没有自己的工位,百来号人挤在一个20平方米的空间里听经理洗脑。白天就在市场上奔波,公司的投入成本几乎只有租金和工资,没有中央空调。早上2小时,晚上加班2小时,纯蒸桑拿。没有任何福利,节假日也没有3倍工资之类的。偶尔会有冲的酸梅汤和西瓜什么的。公司的晋升路径也很有意思:新人—组长—领队—主管—副经理—经理。要求是业绩和团队人数,类似传销模式,把人留下来。新人不能加微信、不能吐槽公司、不能有负面情绪、不能谈恋爱、不能说累。在公司没有任何坐的地方,不能依墙而坐。早上吃早饭在公司外面的安全通道,未到上班时间还会让你吃快些不能磨蹭。总之就是想榨干你。复试的时候,带你的师傅会给你营造一个钱多事少离家近的工作氛围,吹嘘工资有多高、还能吹自己毕业于好大学。然后让你早点来公司、无偿加班、抓住你可能不会走的心思进一步压榨你。总之,大家在找工作的时候一定要擦亮眼睛,避免踩坑!———来自网友
qq乃乃好喝到咩噗茶:不要做没有专业门槛的工作
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
昨天 18:02
好不容易拿到了字节Offer,鼠鼠做后端的,但家里人觉得可能被裁员不稳定,让鼠鼠去投国企,现在好纠结到底该咋选
文档传偷助手:该投就投吧,不过建议别放弃offer 拿到手里的才是最好的
投递字节跳动等公司9个岗位
点赞 评论 收藏
分享
评论
3
3
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务