import heapq
class Solution:
def minimumEffortPath(self, heights):
m, n = len(heights), len(heights[0])
efforts = [[float('inf')] * n for _ in range(m)]
efforts[0][0] = 0
heap = [(0, 0, 0)] # (effort, x, y)
directions = [(-1,0), (1,0), (0,-1), (0,1)]
while heap:
effort, x, y = heapq.heappop(heap)
if x == m - 1 and y == n - 1:
return effort
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < m and 0 <= ny < n:
next_effort = max(effort, abs(heights[x][y] - heights[nx][ny]))
if next_effort < efforts[nx][ny]:
efforts[nx][ny] = next_effort
heapq.heappush(heap, (next_effort, nx, ny))
#include <vector>
#include <queue>
#include <cmath>
#include <utility>
using namespace std;
class Solution {
public:
int minimumEffortPath(vector<vector<int>>& heights) {
int m = heights.size(), n = heights[0].size();
vector<vector<int>> effort(m, vector<int>(n, INT_MAX));
effort[0][0] = 0;
priority_queue<tuple<int,int,int>, vector<tuple<int,int,int>>, greater<tuple<int,int,int>>> pq;
pq.push({0, 0, 0});
vector<pair<int,int>> dirs = {{-1,0},{1,0},{0,-1},{0,1}};
while (!pq.empty()) {
auto [currEff, x, y] = pq.top(); pq.pop();
if (x == m - 1 && y == n - 1) return currEff;
for (auto [dx, dy] : dirs) {
int nx = x + dx, ny = y + dy;
if (nx >= 0 && nx < m && ny >= 0 && ny < n) {
int nextEff = max(currEff, abs(heights[x][y] - heights[nx][ny]));
if (nextEff < effort[nx][ny]) {
effort[nx][ny] = nextEff;
pq.push({nextEff, nx, ny});
}
}
}
}
return 0;
}
};
import java.util.*;
class Solution {
public int minimumEffortPath(int[][] heights) {
int m = heights.length, n = heights[0].length;
int[][] effort = new int[m][n];
for (int[] row : effort) Arrays.fill(row, Integer.MAX_VALUE);
effort[0][0] = 0;
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
pq.offer(new int[]{0, 0, 0}); // {effort, x, y}
int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
while (!pq.isEmpty()) {
int[] curr = pq.poll();
int currEff = curr[0], x = curr[1], y = curr[2];
if (x == m - 1 && y == n - 1) return currEff;
for (int[] dir : dirs) {
int nx = x + dir[0], ny = y + dir[1];
if (nx >= 0 && nx < m && ny >= 0 && ny < n) {
int nextEff = Math.max(currEff, Math.abs(heights[x][y] - heights[nx][ny]));
if (nextEff < effort[nx][ny]) {
effort[nx][ny] = nextEff;
pq.offer(new int[]{nextEff, nx, ny});
}
}
}
}
return 0;
}
}
var minimumEffortPath = function(heights) {
const m = heights.length, n = heights[0].length;
const effort = Array.from({length: m}, () => Array(n).fill(Infinity));
effort[0][0] = 0;
const heap = [[0, 0, 0]]; // [effort, x, y]
const dirs = [[-1,0],[1,0],[0,-1],[0,1]];
while (heap.length > 0) {
heap.sort((a,b) => a[0] - b[0]);
const [currEff, x, y] = heap.shift();
if (x === m-1 && y === n-1) return currEff;
for (const [dx, dy] of dirs) {
const nx = x + dx, ny = y + dy;
if (nx >= 0 && nx < m && ny >= 0 && ny < n) {
const nextEff = Math.max(currEff, Math.abs(heights[x][y] - heights[nx][ny]));
if (nextEff < effort[nx][ny]) {
effort[nx][ny] = nextEff;
heap.push([nextEff, nx, ny]);
}
}
}
}
return 0;
};
Given a 2D grid heights
where each cell heights[i][j]
represents the height of a location, you are to find a path from the top-left cell (0, 0)
to the bottom-right cell (m-1, n-1)
.
You can move in four directions: up, down, left, or right.
The effort of a path is defined as the maximum absolute difference in heights between two consecutive cells along the path. Your goal is to find a path that minimizes this maximum effort.
At first glance, this problem looks similar to finding the shortest path in a grid, but with a twist: instead of summing up distances or costs, we care about the largest "jump" (height difference) we make along any path.
A brute-force approach would be to try all possible paths and, for each, calculate the maximum height difference, keeping the minimum among them. However, this is not feasible for large grids due to the exponential number of possible paths.
We need an efficient way to find the path where the largest step is as small as possible. This is similar to the "minimax path" problem, which suggests using algorithms like Dijkstra's (for shortest path with weights), but with a modification to track the maximum edge weight (effort) along each path instead of the sum.
The key realization is that we can treat each cell as a node, and moving between adjacent cells as an edge with a weight equal to the absolute height difference. We want the path from the start to the end with the minimal possible maximum edge weight.
We solve this problem using a modified version of Dijkstra's algorithm:
This approach ensures we always process the most promising (lowest effort so far) cell next, just like Dijkstra's ensures the shortest path.
We use a 2D array to track the minimum effort to each cell. The heap is initialized with the starting cell at effort 0.
Input:
heights = [
[1,2,2],
[3,8,2],
[5,3,5]
]
Let's trace the algorithm:
The algorithm ensures we never miss a lower-effort path because we always expand the lowest-effort option first.
m
and n
are the grid dimensions.
The "Path With Minimum Effort" problem is a twist on the classic shortest path problem, requiring us to minimize the maximum "jump" (height difference) along any path from the top-left to the bottom-right of a grid. By modeling the grid as a graph and using a modified Dijkstra's algorithm, we efficiently find the path with the smallest possible maximum effort. The approach leverages priority queues and careful bookkeeping, resulting in a solution that is both efficient and elegant.