最小体力消耗路径
难度:
标签:
题目描述
你准备参加一场远足活动。给你一个二维 rows x columns
的地图 heights
,其中 heights[row][col]
表示格子 (row, col)
的高度。一开始你在最左上角的格子 (0, 0)
,且你希望去最右下角的格子 (rows-1, columns-1)
(注意下标从 0 开始编号)。你每次可以往 上,下,左,右 四个方向之一移动,你想要找到耗费 体力 最小的一条路径。
一条路径耗费的 体力值 是路径上相邻格子之间 高度差绝对值 的 最大值 决定的。
请你返回从左上角走到右下角的最小 体力消耗值 。
示例 1:
输入:heights = [[1,2,2],[3,8,2],[5,3,5]] 输出:2 解释:路径 [1,3,5,3,5] 连续格子的差值绝对值最大为 2 。 这条路径比路径 [1,2,2,2,5] 更优,因为另一条路径差值最大值为 3 。
示例 2:
输入:heights = [[1,2,3],[3,8,4],[5,3,5]] 输出:1 解释:路径 [1,2,3,4,5] 的相邻格子差值绝对值最大为 1 ,比路径 [1,3,5,3,5] 更优。
示例 3:

输入:heights = [[1,2,1,1,1],[1,2,1,2,1],[1,2,1,2,1],[1,2,1,2,1],[1,1,1,2,1]] 输出:0 解释:上图所示路径不需要消耗任何体力。
提示:
rows == heights.length
columns == heights[i].length
1 <= rows, columns <= 100
1 <= heights[i][j] <= 106
代码结果
运行时间: 334 ms, 内存: 17.3 MB
/*
* Problem: Find the minimum effort required to travel from the top-left to the bottom-right corner of a grid.
* The effort is defined by the maximum absolute difference in heights between two adjacent cells on the path.
* Solution Approach: This solution uses the same approach as the previous one but leverages Java Streams.
*/
import java.util.*;
import java.util.stream.*;
class SolutionStream {
public int minimumEffortPath(int[][] heights) {
int rows = heights.length;
int cols = heights[0].length;
int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int[][] effort = IntStream.range(0, rows).mapToObj(i -> IntStream.range(0, cols).map(j -> Integer.MAX_VALUE).toArray()).toArray(int[][]::new);
PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
pq.offer(new int[]{0, 0, 0}); // effort, row, col
effort[0][0] = 0;
while (!pq.isEmpty()) {
int[] current = pq.poll();
int currentEffort = current[0];
int row = current[1];
int col = current[2];
if (row == rows - 1 && col == cols - 1) {
return currentEffort;
}
Arrays.stream(directions).forEach(direction -> {
int newRow = row + direction[0];
int newCol = col + direction[1];
if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
int nextEffort = Math.max(currentEffort, Math.abs(heights[newRow][newCol] - heights[row][col]));
if (nextEffort < effort[newRow][newCol]) {
effort[newRow][newCol] = nextEffort;
pq.offer(new int[]{nextEffort, newRow, newCol});
}
}
});
}
return 0; // Unreachable code, since the destination will always be reached
}
}
解释
方法:
该题解采用的是一个优化的 Dijkstra 算法,通过一个最小堆来维持当前探索的路径中的最大高度差的最小值。首先,定义一个二维数组 `dps` 来存储到每个点的最小体力消耗值,并初始化终点的体力消耗值为0。使用优先队列(最小堆)来按体力消耗递增的顺序处理每个格子。对于当前格子,我们检查它的四个邻居,如果这个邻居没有被访问过,并且通过当前格子到该邻居的路径可以得到更小的体力消耗值,我们就更新邻居的体力消耗值,并将其加入堆中。这个过程重复,直到堆为空,或者我们访问到起点。最终,起点的 `dps` 值就是我们要找的答案。
时间复杂度:
O((MN) * log(MN))
空间复杂度:
O(MN)
代码细节讲解
🦆
在该算法中,如何保证每次从堆中弹出的节点确实是当前路径中体力消耗最小的节点?
▷🦆
优化的 Dijkstra 算法在处理每个节点时如何确定是否需要更新邻居节点的体力消耗值?
▷🦆
算法实现中提到了`如果当前计算的消耗不优于已有的,则跳过`,请问这种情况下的'不优于'是如何定义的?
▷🦆
为什么在实现中初始化堆时,只将终点(`[0, n-1, m-1]`)加入堆中,而不是起点或其他任何点?
▷