Loading problems...
TL;DR: The optimal solution uses a Min-Heap to track the largest height differences encountered so far, ensuring that limited ladders are allocated to the steepest climbs while bricks cover the smaller remaining jumps.
In the LeetCode 1642 problem, "Furthest Building You Can Reach," you are tasked with navigating an array of building heights. Moving from a taller building to a shorter one is free. However, moving from a shorter building to a taller one requires resources: either one ladder (which covers any height difference) or a number of bricks equal to the height difference. Given a finite supply of bricks and ladders, the goal is to determine the maximum index (furthest building) reachable starting from index 0.
This is a popular interview question that tests your ability to make optimal resource allocation decisions using greedy strategies and priority queues.
The naive approach involves exploring every possible decision at each step. When encountering a climb (where the next building is taller), we branch into two possibilities: using a ladder or using bricks. This effectively creates a decision tree where we traverse every valid path until resources are exhausted, returning the maximum depth reached.
Pseudo-code:
text
function solve(index, bricks, ladders):
if index is last building: return index
diff = heights[index+1] - heights[index]
if diff <= 0:
return solve(index + 1, bricks, ladders)
res = index
if ladders > 0:
res = max(res, solve(index + 1, bricks, ladders - 1))
if bricks >= diff:
res = max(res, solve(index + 1, bricks - diff, ladders))
return resTime Complexity: , where is the number of buildings. In the worst case, every step is a climb, and we branch twice.
Why it fails: With heights.length up to , an exponential solution will immediately trigger a Time Limit Exceeded (TLE). We need a solution that is close to linear time.
The key intuition for LeetCode 1642 is understanding the relative value of the resources. A ladder is infinitely valuable in terms of height capacity; it costs the same (1 unit) to climb a difference of 1 or a difference of 1,000. Bricks, however, are consumed linearly based on height.
To maximize distance, ladders should always be used for the largest climbs encountered, while bricks should be used for the smaller climbs.
However, as we iterate through the array, we do not know if a future climb will be larger than the current one. We cannot make a perfect permanent decision immediately. Instead, we can use a "tentative" greedy approach:
This strategy enforces the invariant that at any point , the ladders are covering the largest jumps seen so far, and bricks have paid for the rest.
Visual Description: Imagine a container (the Min-Heap) that can hold exactly items. As we walk through the buildings, every time we face a climb, we toss the height difference into this container. If the container overflows (size becomes ), the smallest item "spills out." The item that spills out represents the climb that is no longer worthy of a ladder compared to the others. We must then pay the cost of that spilled item using our bricks. If we cannot afford the bricks, we stop.

Let's trace heights = [4, 2, 7, 6, 9, 14, 12], bricks = 5, ladders = 1.
Start at index 0 (height 4). Move to index 1 (height 2).
diff = 2 - 4 = -2. No cost.[]. Bricks: 5.At index 1 (height 2). Move to index 2 (height 7).
diff = 7 - 2 = 5. Climb.5 to Heap. Heap: [5].At index 2 (height 7). Move to index 3 (height 6).
diff = 6 - 7 = -1. No cost.At index 3 (height 6). Move to index 4 (height 9).
diff = 9 - 6 = 3. Climb.3 to Heap. Heap: [3, 5] (conceptually).3 from Heap. Heap is now [5]. Ladder stays with the 5-jump.bricks = 5 - 3 = 2.At index 4 (height 9). Move to index 5 (height 14).
diff = 14 - 9 = 5. Climb.5 to Heap. Heap: [5, 5].5.bricks = 2 - 5 = -3.Return current index 4.
The correctness relies on the "exchange argument" common in greedy proofs. Suppose there is an optimal solution that reaches index . In this solution, suppose a ladder is used for a climb of height and bricks are used for a climb of height , where .
We could swap these resources: use bricks for and a ladder for .
Since , the new cost uses fewer bricks to reach the same point. Therefore, to maximize the distance (or minimize brick usage for a fixed distance), ladders must always be assigned to the largest set of climbs. Our Min-Heap approach guarantees that for any set of climbs encountered, the largest climbs are covered by ladders and the smallest climbs are covered by bricks.
cpp#include <vector> #include <queue> #include <functional> using namespace std; class Solution { public: int furthestBuilding(vector<int>& heights, int bricks, int ladders) { // Min-heap to store the height differences covered by ladders priority_queue<int, vector<int>, greater<int>> minHeap; for (int i = 0; i < heights.size() - 1; ++i) { int diff = heights[i + 1] - heights[i]; // If the next building is taller, we need to use a resource if (diff > 0) { // Tentatively use a ladder for this climb minHeap.push(diff); // If we have used more ladders than available if (minHeap.size() > ladders) { // Remove the ladder from the smallest climb in the heap // and use bricks for that climb instead bricks -= minHeap.top(); minHeap.pop(); } // If bricks become negative, we can't proceed if (bricks < 0) { return i; } } } // If we complete the loop, we reached the last building return heights.size() - 1; } };
javaimport java.util.PriorityQueue; class Solution { public int furthestBuilding(int[] heights, int bricks, int ladders) { // Min-heap to keep track of the largest jumps encountered so far PriorityQueue<Integer> minHeap = new PriorityQueue<>(); for (int i = 0; i < heights.length - 1; i++) { int diff = heights[i + 1] - heights[i]; if (diff > 0) { // Add the current climb to the heap (simulating ladder usage) minHeap.add(diff); // If we exceed the number of ladders, we must pay for the // smallest climb in the heap using bricks. if (minHeap.size() > ladders) { bricks -= minHeap.poll(); } // If we run out of bricks, we cannot move to i + 1 if (bricks < 0) { return i; } } } return heights.length - 1; } }
pythonimport heapq class Solution: def furthestBuilding(self, heights: list[int], bricks: int, ladders: int) -> int: min_heap = [] # Stores the climbs we are currently using ladders for for i in range(len(heights) - 1): diff = heights[i + 1] - heights[i] if diff > 0: # Push current climb to heap heapq.heappush(min_heap, diff) # If we have more climbs in heap than ladders available if len(min_heap) > ladders: # Pop the smallest climb; this one must be paid with bricks smallest_climb = heapq.heappop(min_heap) bricks -= smallest_climb # If bricks drop below zero, we can't reach i + 1 if bricks < 0: return i return len(heights) - 1
Time Complexity:
heights array once ( steps).Space Complexity:
The Heap - Scheduling / Minimum Cost pattern is versatile. Understanding how to maintain a "Top K" set or dynamically retrieve the minimum/maximum element is crucial for these problems:
Why does the naive DFS/Recursion solution TLE?
Why can't we use Dynamic Programming (DP)?
dp[index][bricks].bricks constraint can be up to . A DP table of size would consume far too much memory and time.Why use a Min-Heap instead of a Max-Heap?