华为OD机试中的“小明周末爬山”题目是一道经典的路径搜索和动态规划问题。以下是对该题目的详细解析:
一、题目描述
周末小明准备去爬山锻炼,地图上的0代表平地,山的高度使用1到9来表示。小明每次爬山或下山高度只能相差k及k以内,且每次只能上下左右一个方向上移动一格。小明从左上角(0,0)位置出发,目标是找到他能爬到的最高峰,并输出该峰的高度以及到达该峰的最短步数。如果有多座同高度的山峰,则输出步数较短的那座。如果没有可以爬的山峰,则高度和步数都返回0。
二、输入描述
第一行输入m、n、k(空格分隔),代表m×n的二维山地图,其中m为行数,n为列数,k为小明每次爬山或下山高度差的最大值。
接下来输入山地图,一共m行n列,均以空格分隔。
三、取值范围
- 0 < m ≤ 500
- 0 < n ≤ 500
- 0 < k < 5
四、输出描述
输出小明能爬到的最高峰的高度以及到达该峰的最短步数,以空格分隔。
五、解题思路
-
广度优先搜索(BFS):
- 使用一个队列来进行广度优先搜索。
- 定义一个状态结构体,包含当前位置、当前高度和当前步数。
- 将起始状态(0,0,0,0)加入队列,并标记为已访问。
- 在搜索过程中,根据当前状态,尝试向上下左右四个方向移动,并计算新的高度和步数。
- 如果新的高度大于当前记录的最高峰高度,或者高度相同但步数更短,则更新最高峰高度和最短步数。
- 如果新的位置是有效的(在地图范围内内,且高度差在k以内),且未被访问过,则将其加入队列,并标记为已访问。
- 重复上述步骤,直到队列为空。
-
动态规划(DP):
- 定义一个二维数组dp,其中dp[i][j]表示到达位置(i,j)时的最高山峰高度以及到达该位置的最短步数(可以用一个pair或结构体来表示)。
- 初始化dp数组,将起始位置(0,0)的dp值设为(0,0)。
- 从起始位置开始,遍历地图上的每个位置。
- 对于每个位置(i,j),尝试从上下左右四个方向到达该位置,并计算到达该位置时的最高山峰高度以及最短步数。
- 更新dp[i][j]为四个方向中的最优值。
- 遍历完成后,找到dp数组中的最大值以及对应的步数。
六、示例
示例1:
输入:
5 4 1
0 1 2 0
1 0 0 0
1 0 1 2
1 3 1 0
0 0 0 9
输出:
2 2
解释:
能爬到的最高峰在(0,2)位置,高度为2,最短路径为(0,0)-(0,1)-(0,2),最短步数为2。
示例2:
输入:
5 4 3
0 0 0 0
0 0 0 0
9 0 0 0
0 0 0 0
0 0 0 9
输出:
0 0
解释:
每次爬山距离3,无法爬到山峰上,步数为0。
七、代码实现
下面是一个用Java实现的广度优先搜索(BFS)算法来解决“小明周末爬山”问题的代码示例。该算法使用一个队列来存储待探索的状态,并使用一个布尔数组来记录已经访问过的位置,以避免重复访问。
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
class MountainPeak {
int x, y, height, steps;
MountainPeak(int x, int y, int height, int steps) {
this.x = x;
this.y = y;
this.height = height;
this.steps = steps;
}
}
public class ClimbingMountain {
private static final int[] DX = {-1, 1, 0, 0}; // 上下左右移动的方向向量
private static final int[] DY = {0, 0, -1, 1};
/**
* 主函数,解决山峰地图中的最大高度和最小步数问题
* 通过广度优先搜索算法,找到从左上角到右下角的最大高度差不超过k的路径,并计算路径中的最大高度和最小步数
* @param args 命令行参数,未使用
*/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取输入的行数、列数和最大高度差
int m = scanner.nextInt();
int n = scanner.nextInt();
int k = scanner.nextInt();
// 初始化山峰地图
int[][] mountainMap = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
mountainMap[i][j] = scanner.nextInt();
}
}
// 初始化最大高度和最小步数
int maxHeight = 0;
int minSteps = Integer.MAX_VALUE;
// 初始化访问标记数组
boolean[][] visited = new boolean[m][n];
// 使用队列存储待访问的山峰
Queue<MountainPeak> queue = new LinkedList<>();
// 将起始点加入队列,并标记为已访问
queue.add(new MountainPeak(0, 0, mountainMap[0][0], 0));
visited[0][0] = true;
// 使用广度优先搜索遍历山峰
while (!queue.isEmpty()) {
MountainPeak peak = queue.poll();
// 更新最大高度和最小步数
if (peak.height > maxHeight || (peak.height == maxHeight && peak.steps < minSteps)) {
maxHeight = peak.height;
minSteps = peak.steps;
}
// 遍历四个方向
for (int i = 0; i < 4; i++) {
int newX = peak.x + DX[i];
int newY = peak.y + DY[i];
// 检查新位置是否有效,并计算高度差
if (newX >= 0 && newX < m && newY >= 0 && newY < n && !visited[newX][newY] &&
Math.abs(mountainMap[newX][newY] - peak.height) <= k) {
// 标记新位置为已访问,并加入队列
visited[newX][newY] = true;
queue.add(new MountainPeak(newX, newY, mountainMap[newX][newY], peak.steps + 1));
}
}
}
// 输出结果
if (maxHeight == 0 && minSteps == Integer.MAX_VALUE) {
System.out.println("0 0");
} else {
System.out.println(maxHeight + " " + minSteps);
}
// 关闭扫描器
scanner.close();
}
}
代码说明:
-
MountainPeak 类:
- 这是一个简单的类,用于存储山峰的位置(x, y)、高度(height)和到达该位置的步数(steps)。
-
DX 和 DY 数组:
- 这两个数组定义了上下左右四个方向的移动向量,用于在地图上移动位置。
-
主方法:
- 读取输入数据,包括地图的尺寸(m, n)和每次爬山或下山高度差的最大值(k)。
- 读取地图数据,并存储在二维数组
mountainMap中。 - 初始化最高山峰高度
maxHeight为0,最短步数minSteps为Integer.MAX_VALUE。 - 使用布尔数组
visited来记录已经访问过的位置。 - 创建一个队列
queue,并将起始位置(0,0)的状态加入队列。 - 使用广度优先搜索算法遍历地图,更新最高山峰高度和最短步数。
- 遍历完成后,根据最高山峰高度和最短步数的值输出结果。
请注意,这个实现假设输入数据是有效的,并且没有进行额外的输入验证。在实际应用中,应该添加适当的输入验证来确保程序的健壮性。
八、注意事项
- 在搜索或动态规划过程中,要注意边界条件的处理,确保不会越界访问。
- 在使用动态规划时,要注意状态的转移方程和初始化条件。
- 在输出结果时,要确保格式正确,高度和步数之间用空格分隔。
通过以上解题思路和方法,可以高效地解决“小明周末爬山”这道华为OD机试真题。

5143

被折叠的 条评论
为什么被折叠?



