华为OD机试_2025 B卷_小华地图寻宝(Python,100分)(附详细解题思路)
文章目录
- 题目描述
- 小华寻宝问题:简单直观的BFS解法
- 核心解题思路
- 关键问题分析
- 简单解法:广度优先搜索(BFS)
- 为什么BFS适合?
- 算法步骤详解
- 1. 数位之和计算
- 2. 输入处理
- 3. 特殊情况处理
- 4. BFS核心
- 5. 输出结果
- 完整代码实现
- 示例验证
- BFS执行过程
- 关键算法点
- 1. 方向处理
- 2. 访问标记
- 3. 边界检查
- 边界情况处理
- 为什么BFS比DFS更合适?
- 总结与思考
题目描述
小华按照地图去寻宝,地图上被划分成 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 |
说明 | 无 |
小华寻宝问题:简单直观的BFS解法
核心解题思路
关键问题分析
小华需要从起点 (0,0) 出发,访问所有满足条件的格子:
- 只能进入坐标数位之和 ≤ k 的格子
- 只能在相邻的满足条件的格子间移动
- 每个满足条件的格子有1克黄金
简单解法:广度优先搜索(BFS)
使用BFS可以高效解决网格遍历问题:
- 从起点 (0,0) 开始
- 检查当前格子是否满足条件
- 向四个方向扩展
- 统计访问过的所有满足条件的格子数量
为什么BFS适合?
- 保证访问所有可达格子:BFS按层遍历,不会遗漏
- 避免重复访问:使用标记数组记录已访问格子
- 高效实现:网格最大50×50=2500个格子,BFS完全可行
- 直观易懂:模拟小华实际移动过程
算法步骤详解
1. 数位之和计算
def digit_sum(i, j):"""计算坐标(i,j)的数位之和"""total = 0for num in [i, j]:while num:total += num % 10num //= 10return total
2. 输入处理
m, n, k = map(int, input().split())
3. 特殊情况处理
- 网格为空时直接返回0
- 起点不满足条件时返回0
4. BFS核心
from collections import deque# 初始化
visited = [[False] * n for _ in range(m)]
queue = deque()
gold_count = 0# 起点检查
if digit_sum(0, 0) <= k:queue.append((0, 0))visited[0][0] = True# BFS主循环
while queue:i, j = queue.popleft()gold_count += 1 # 获得黄金# 四个方向:上、下、左、右for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:ni, nj = i + dx, j + dy# 检查新坐标是否有效if 0 <= ni < m and 0 <= nj < n:# 检查是否访问过且满足条件if not visited[ni][nj] and digit_sum(ni, nj) <= k:visited[ni][nj] = Truequeue.append((ni, nj))
5. 输出结果
print(gold_count)
完整代码实现
from collections import dequedef digit_sum(i, j):"""计算坐标(i,j)的数位之和"""total = 0for num in [i, j]:while num:total += num % 10num //= 10return total# 输入处理
m, n, k = map(int, input().split())# 处理网格为空的情况
if m == 0 or n == 0:print(0)exit()# 初始化BFS数据结构
visited = [[False] * n for _ in range(m)]
queue = deque()
gold_count = 0# 检查起点(0,0)
if digit_sum(0, 0) <= k:queue.append((0, 0))visited[0][0] = True# BFS主循环
while queue:i, j = queue.popleft()gold_count += 1 # 获得当前格子的黄金# 遍历四个方向:上、下、左、右directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]for dx, dy in directions:ni, nj = i + dx, j + dy # 新坐标# 检查新坐标是否在网格内if 0 <= ni < m and 0 <= nj < n:# 检查是否未访问且满足条件if not visited[ni][nj] and digit_sum(ni, nj) <= k:visited[ni][nj] = Truequeue.append((ni, nj))# 输出结果
print(gold_count)
示例验证
用例1:输入 5 4 7 → 输出 20
- 网格大小:5行4列(20个格子)
- 所有格子数位之和 ≤ 7(最大为4+3=7)
- BFS会访问所有20个格子
- 输出20正确
用例2:输入 40 40 18 → 输出 1484
- BFS遍历所有满足条件的格子
- 计算结果为1484,符合题目要求
BFS执行过程
- 初始化:起点(0,0)入队
- 循环开始:出队(0,0),黄金+1
- 扩展邻居:(0,1)、(1,0)入队
- 继续出队:处理(0,1),扩展邻居
- 层层扩展:直到所有可达格子被访问
- 结束条件:队列为空
关键算法点
1. 方向处理
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
使用元组表示四个移动方向:
- (0,1):向右移动
- (0,-1):向左移动
- (1,0):向下移动
- (-1,0):向上移动
2. 访问标记
visited = [[False] * n for _ in range(m)]
- 创建二维数组记录访问状态
- 避免重复访问,提高效率
3. 边界检查
if 0 <= ni < m and 0 <= nj < n:
确保新坐标在网格范围内
边界情况处理
- 网格为空:
m == 0 or n == 0
时直接输出0 - 起点不满足条件:k < 0 时输出0(但k≥0,起点数位和=0,所以不会发生)
- 孤立区域:BFS只访问连通区域,自动处理孤立区
为什么BFS比DFS更合适?
- 非递归实现:避免递归深度过大
- 更少内存占用:队列比调用栈更节省空间
- 层序访问:更符合网格遍历特点
- 避免堆栈溢出:网格最大2500个点,DFS递归深度可能达2500级
总结与思考
通过这个解法,我们学习到:
- BFS的基本框架:
- 队列初始化
- 访问标记
- 方向扩展
- 网格问题的处理技巧:
- 方向数组简化移动
- 边界检查确保安全
- 访问标记避免重复
- 问题分析能力:
- 识别连通区域特性
- 选择合适遍历算法
- 处理边界条件
BFS解法时间复杂度 O(m×n),空间复杂度 O(m×n),在题目约束下完全可行。代码简洁直观,适合初学者理解和实现。
小技巧:网格遍历问题优先考虑BFS,它提供了一种系统化、可预测的访问顺序,确保不会遗漏任何可达点。