Lesson 170 of 250
1631. Path With Minimum Effort
NeetCode: Path With Minimum Effort · LeetCode: 1631. Path With Minimum Effort · Difficulty: Medium
You are given a rows x cols grid heights. Starting at the top-left cell, move up, down, left or right to reach the bottom-right cell. The effort of a route is the maximum absolute height difference between two consecutive cells on it. Return the minimum effort over all routes.
Example
Input: heights = [[1,2,2],[3,8,2],[5,3,5]] Output: 2
Input: heights = [[1,2,3],[3,8,4],[5,3,5]] Output: 1
Input: heights = [[7]] Output: 0This is a shortest-path problem in which a path costs the maximum of its edge weights instead of their sum. The cost is monotone (extending a path never lowers it), which is what Dijkstra requires.
Brute force: sweep the grid relaxing until stable
Keep effort[r][c], the smallest known effort to reach each cell. Sweep over every cell and, for each neighbour, propose max(effort[cell], |height difference|); keep it if it is smaller. Repeat until a sweep changes nothing. This is Bellman-Ford style relaxation and may need many full sweeps.
The difference of two heights is an int, and Integer.MAX_VALUE marks "not reached yet". Very large heights would need long.
import java.util.Arrays;
class Solution {
public int minimumEffortPath(int[][] heights) {
int rows = heights.length, cols = heights[0].length;
int[][] effort = new int[rows][cols];
for (int[] row : effort) Arrays.fill(row, Integer.MAX_VALUE);
effort[0][0] = 0;
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
boolean changed = true;
while (changed) {
changed = false;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (effort[r][c] == Integer.MAX_VALUE) continue;
for (int[] d : dirs) {
int nr = r + d[0], nc = c + d[1];
if (nr < 0 || nc < 0 || nr >= rows || nc >= cols) continue;
int cand = Math.max(effort[r][c], Math.abs(heights[r][c] - heights[nr][nc]));
if (cand < effort[nr][nc]) {
effort[nr][nc] = cand;
changed = true;
}
}
}
}
}
return effort[rows - 1][cols - 1];
}
}
public class Main {
public static void main(String[] args) {
Solution s = new Solution();
System.out.println(s.minimumEffortPath(new int[][] {{1, 2, 2}, {3, 8, 2}, {5, 3, 5}}));
System.out.println(s.minimumEffortPath(new int[][] {{1, 2, 3}, {3, 8, 4}, {5, 3, 5}}));
System.out.println(s.minimumEffortPath(new int[][] {{7}}));
}
}2
1
0Time: O(P * m * n) where P is the number of sweeps (up to m * n in the worst case). Space: O(m * n).
Optimal: Dijkstra with a max-edge cost
Use a min-heap keyed by effort. The popped cell with the smallest effort has its final value, because any other route to it would have to pass through a heap entry that is at least as large. For each neighbour the new effort is max(current effort, step difference); push it if it improves the stored value. Each cell enters the heap a bounded number of times (once per improving edge), giving O(mn log mn).
Java's PriorityQueue is a min-heap. Each entry is an int[]{effort, row, column} ordered with Integer.compare on the effort.
import java.util.Arrays;
import java.util.PriorityQueue;
class Solution {
public int minimumEffortPath(int[][] heights) {
int rows = heights.length, cols = heights[0].length;
int[][] effort = new int[rows][cols];
for (int[] row : effort) Arrays.fill(row, Integer.MAX_VALUE);
effort[0][0] = 0;
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> Integer.compare(a[0], b[0]));
heap.add(new int[] {0, 0, 0});
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
while (!heap.isEmpty()) {
int[] top = heap.poll();
int e = top[0], r = top[1], c = top[2];
if (e > effort[r][c]) continue; // stale entry
if (r == rows - 1 && c == cols - 1) return e;
for (int[] d : dirs) {
int nr = r + d[0], nc = c + d[1];
if (nr < 0 || nc < 0 || nr >= rows || nc >= cols) continue;
int cand = Math.max(e, Math.abs(heights[r][c] - heights[nr][nc]));
if (cand < effort[nr][nc]) {
effort[nr][nc] = cand;
heap.add(new int[] {cand, nr, nc});
}
}
}
return 0;
}
}
public class Main {
public static void main(String[] args) {
Solution s = new Solution();
System.out.println(s.minimumEffortPath(new int[][] {{1, 2, 2}, {3, 8, 2}, {5, 3, 5}}));
System.out.println(s.minimumEffortPath(new int[][] {{1, 2, 3}, {3, 8, 4}, {5, 3, 5}}));
System.out.println(s.minimumEffortPath(new int[][] {{7}}));
}
}2
1
0Time: O(m * n * log(m * n)). Space: O(m * n).
