题目描述
小华按照地图去寻宝,地图上被划分成 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 |
解题思路
题目规则再梳理
- 地图是 m行 n列 的方格:
- 横坐标(列)范围:0 ~ n-1
- 纵坐标(行)范围:0 ~ m-1
-
黄金判定规则:对坐标 (x, y),x 的每一位数字之和 + y 的每一位数字之和 ≤ k,这个格子就有 1 克黄金,能走;反之格子危险,绝对不能进入。
举个例子:坐标(14, 23)
x=14 数位和:1+4=5,y=23 数位和:2+3=5,总和10。如果 k≥10,这个格子可走、有黄金。
-
移动规则:从起点 (0, 0) 出发,上下左右 4 个方向每次只能走 1 格,不能斜着走。
-
最终目标:统计从起点出发,所有能走到的、符合黄金条件的格子总数(每个格子 1 克黄金)。
-
核心解题逻辑
步骤 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;
}
}