小华地图寻宝

题目描述

小华按照地图去寻宝,地图上被划分成 m 行和 n 列的方格,横纵坐标范围分别是 [0, n-1] 和 [0, m-1]。

在横坐标和纵坐标的数位之和不大于 k 的方格中存在黄金(每个方格中仅存在一克黄金),但横坐标和纵坐标数位之和大于 k 的方格存在危险不可进入。小华从入口 (0,0) 进入,任何时候只能向左,右,上,下四个方向移动一格。

请问小华最多能获得多少克黄金?

输入描述

坐标取值范围如下:

0 ≤ m ≤ 50

0 ≤ n ≤ 50

k 的取值范围如下:

0 ≤ k ≤ 100

输入中包含3个字数,分别是m, n, k

输出描述

输出小华最多能获得多少克黄金

用例

输入 40 40 18
输出 1484
输入 5 4 7
输出 20

解题思路

题目规则再梳理

  1. 地图是 m行 n列 的方格:
  • 横坐标(列)范围:0 ~ n-1
  • 纵坐标(行)范围:0 ~ m-1
  1. 黄金判定规则:对坐标 (x, y),x 的每一位数字之和 + y 的每一位数字之和 ≤ k,这个格子就有 1 克黄金,能走;反之格子危险,绝对不能进入。

    举个例子:坐标(14, 23)

    x=14 数位和:1+4=5,y=23 数位和:2+3=5,总和10。如果 k≥10,这个格子可走、有黄金。

  2. 移动规则:从起点 (0, 0) 出发,上下左右 4 个方向每次只能走 1 格,不能斜着走。

  3. 最终目标:统计从起点出发,所有能走到的、符合黄金条件的格子总数(每个格子 1 克黄金)。

  4. 核心解题逻辑

步骤 1:先写「数位和计算工具方法」

我们需要一个函数,输入一个数字,能算出它所有位上的数字相加的和。比如输入25,返回2+5=7;输入9,返回9;输入0,返回0。

步骤 2:用 BFS 广度优先搜索遍历所有可达格子

BFS 就是一层一层往外扩散遍历,非常适合这种网格四方向走路、找所有可达点的场景,逻辑就像水从起点慢慢往四周蔓延:

起点固定是 (0, 0),首先判断起点本身是否符合条件(0的数位和+0的数位和=0,只要 k≥0 就一定合法)。

准备一个队列,用来存放接下来要去访问的格子坐标;再准备一个访问标记数组,记录哪些格子已经走过了,避免重复走、重复统计黄金(防止原地来回绕圈死循环)。

循环遍历队列:每次拿出队首的格子,黄金计数 + 1;然后往上下左右 4 个方向分别探索相邻格子。

对每一个相邻格子,做 3 个合法性校验,全部满足才入队:

① 坐标没有越界(在地图范围内:行0<=y<m,列0<=x<n)

② 这个格子还没有被访问过

③ 该坐标的横纵坐标数位之和 ≤ k(有黄金、不危险)

直到队列为空,所有能走到的格子全部遍历完毕,最终的计数就是答案。

为什么选 BFS 而不是 DFS?

BFS(广度优先):层序遍历,从起点一圈圈扩散,逻辑直观,天然适合网格可达性统计,不容易出现深度递归栈溢出。

DFS(深度优先):一条路走到黑再回溯,虽然也能做,但网格最大 50*50,递归深度过高有栈风险,且代码可读性不如 BFS。

java实现

java 复制代码
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

/**
 * 小华寻宝问题(网格BFS广度优先搜索)
 * 题目:从(0,0)出发,四方向移动,横纵坐标数位和<=k的格子有黄金,统计所有可达格子总数
 */
public class TreasureHunt {
    // 上下左右 4个移动方向的坐标偏移量
    private static final int[][] DIRS = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

    public static void main(String[] args) {
        // 1. 控制台输入:依次输入 m(行数), n(列数), k(数位和上限)
        Scanner scanner = new Scanner(System.in);
        int m = scanner.nextInt();
        int n = scanner.nextInt();
        int k = scanner.nextInt();
        scanner.close();

        // 2. 调用核心方法计算结果,并输出
        int maxGold = getMaxGold(m, n, k);
        System.out.println(maxGold);
    }

    /**
     * 核心方法:计算最多能获得的黄金数量
     * @param m 地图总行数,纵坐标范围 [0, m-1]
     * @param n 地图总列数,横坐标范围 [0, n-1]
     * @param k 横纵坐标数位和上限
     * @return 可达的黄金格子总数
     */
    public static int getMaxGold(int m, int n, int k) {
        // 边界情况:地图为空,直接返回0
        if (m == 0 || n == 0) {
            return 0;
        }

        // 访问标记数组:visited[y][x] = true 代表坐标(x,y)已经走过,避免重复遍历
        boolean[][] visited = new boolean[m][n];
        // BFS队列,存储待遍历的坐标 (横坐标x, 纵坐标y)
        Queue<int[]> queue = new LinkedList<>();

        // 初始化:起点(0,0),先判断起点是否合法
        if (countBitSum(0) + countBitSum(0) <= k) {
            queue.offer(new int[]{0, 0});
            visited[0][0] = true;
        }

        int goldCount = 0; // 黄金总数统计

        // BFS主循环:队列不为空就一直遍历
        while (!queue.isEmpty()) {
            // 取出队首当前格子坐标
            int[] curr = queue.poll();
            int x = curr[0]; // 横坐标(列)
            int y = curr[1]; // 纵坐标(行)

            // 当前格子合法有黄金,计数+1
            goldCount++;

            // 遍历上下左右4个方向,探索相邻格子
            for (int[] dir : DIRS) {
                int newX = x + dir[0];
                int newY = y + dir[1];

                // 四重合法性校验,全部满足才可以入队访问
                // 1. 横坐标不越界:0 <= newX < n
                // 2. 纵坐标不越界:0 <= newY < m
                // 3. 该格子从未访问过
                // 4. 横纵坐标数位之和 不超过k(安全、有黄金)
                if (newX >= 0 && newX < n
                        && newY >= 0 && newY < m
                        && !visited[newY][newX]
                        && (countBitSum(newX) + countBitSum(newY) <= k)) {
                    // 标记已访问,加入队列待遍历
                    visited[newY][newX] = true;
                    queue.offer(new int[]{newX, newY});
                }
            }
        }

        return goldCount;
    }

    /**
     * 工具方法:计算一个数字所有位上的数字之和
     * 例如:num=25 → 2+5=7;num=0 → 0;num=103 →1+0+3=4
     * @param num 输入数字
     * @return 数位之和
     */
    private static int countBitSum(int num) {
        int sum = 0;
        while (num > 0) {
            sum += num % 10; // 取个位数字累加
            num = num / 10;  // 去掉个位数字
        }
        return sum;
    }
}
相关推荐
贾斯汀玛尔斯2 小时前
每天学一个算法--单调栈(Monotonic Stack)
运维·服务器·算法
ZPC82102 小时前
ROS2 速度远快于 UDP的完整方案(同机节点)
人工智能·算法·计算机视觉·机器人
khalil10202 小时前
代码随想录算法训练营Day-34动态规划03 | 01背包问题 二维、01背包问题 一维、416. 分割等和子集
数据结构·c++·算法·leetcode·动态规划·背包问题·01背包
华清远见IT开放实验室2 小时前
AI 算法核心知识清单(深度实战版2)
人工智能·深度学习·算法·机器学习·ai·模型训练
点云侠2 小时前
隧道中线提取的优化方法
c++·算法·最小二乘法
minji...3 小时前
Linux 线程同步与互斥(五) 日志,线程池
linux·运维·服务器·开发语言·c++·算法
python_DONG3 小时前
响应面法(Response Surface Methodology, RSM)单目标优化算法
算法·数学建模
6Hzlia3 小时前
【Hot 100 刷题计划】 LeetCode 108. 将有序数组转换为二叉搜索树 | C++ 分治法详解
c++·算法·leetcode
itzixiao4 小时前
L1-051 打折(5分)[java][python]
java·python·算法