
Problem Statement
In this scenario, you are given an array of integers named heights
where each element represents the height of buildings in a sequential manner. You begin your traversal from the first building and aim to advance as far as possible to the subsequent buildings. The tools at your disposal for this traversal include a certain number of bricks
and ladders
. The strategic use of these tools is crucial as you decide how to move from one building to the next.
The challenge involves moving from building i
to i+1
. If building i
is taller than or equal to building i+1
, you proceed without using any tools. Conversely, if building i
is shorter than i+1
, you will need to either use a ladder or a specific number of bricks (exactly the height difference between buildings i+1
and i
). The objective is to determine the furthest building index you can reach using the bricks and ladders optimally, starting from building index 0
.
Examples
Example 1
Input:
heights = [4,2,7,6,9,14,12], bricks = 5, ladders = 1
Output:
4
Explanation:
Starting at building 0, you can follow these steps: - Go to building 1 without using ladders nor bricks since 4 >= 2. - Go to building 2 using 5 bricks. You must use either bricks or ladders because 2 < 7. - Go to building 3 without using ladders nor bricks since 7 >= 6. - Go to building 4 using your only ladder. You must use either bricks or ladders because 6 < 9. It is impossible to go beyond building 4 because you do not have any more bricks or ladders.
Example 2
Input:
heights = [4,12,2,7,3,18,20,3,19], bricks = 10, ladders = 2
Output:
7
Example 3
Input:
heights = [14,3,19,3], bricks = 17, ladders = 0
Output:
3
Constraints
1 <= heights.length <= 105
1 <= heights[i] <= 106
0 <= bricks <= 109
0 <= ladders <= heights.length
Approach and Intuition
The key to solving this problem lies in efficiently deciding when to use bricks and when to use ladders, given that ladders can overcome any height difference but bricks are limited by the quantity you possess. Here's a step-by-step breakdown of the approach:
- Start from the first building and proceed to assess each subsequent building.
- If the next building is lower or at the same height, simply move forward without consuming any resources.
- If the next building is taller, calculate the difference in height, which indicates the number of bricks required to proceed.
- Decide whether to use bricks or a ladder:
- Prefer using bricks for smaller differences and save ladders for larger or critical ascents, since ladders are not abundant and can cover any height difference.
- If running low on bricks but a ladder is available, use the ladder.
- Continually adjust your count of bricks and ladders based on what was used.
- If at any point both resources are depleted and you cannot progress to a higher building, terminate the traversal.
Strategy for Optimal Use of Resources:
- Keeping track of the largest climbs: Since ladders bypass the height difference completely, they should optimally be used for the largest height differences encountered. Thus, maintaining a priority of obstacles (height differences) that have been overcome using bricks can help in retroactively deciding to switch to ladders for them and conserving bricks for smaller climbs.
- Pricing a Ladder vs. Bricks: Since ladders are more versatile (any height can be scaled with one ladder), using a ladder for extraordinary height differences makes more sense, especially when brick supply starts running low.
By following this methodical approach, you can effectively manage your resources and maximize the distance you can cover across the buildings.
Solutions
- Java
- Python
class Solution {
public int maxReachableBuilding(int[] buildingHeights, int totalBricks, int totalLadders) {
int minimum = Integer.MAX_VALUE;
int maximum = Integer.MIN_VALUE;
for (int i = 0; i < buildingHeights.length - 1; i++) {
int requiredClimb = buildingHeights[i + 1] - buildingHeights[i];
if (requiredClimb <= 0) {
continue;
}
minimum = Math.min(minimum, requiredClimb);
maximum = Math.max(maximum, requiredClimb);
}
if (minimum == Integer.MAX_VALUE) {
return buildingHeights.length - 1;
}
while (minimum <= maximum) {
int mid = minimum + (maximum - minimum) / 2;
int[] evalResult = evaluateThreshold(buildingHeights, totalBricks, totalLadders, mid);
int lastBuilding = evalResult[0];
int remainingLadders = evalResult[1];
int remainingBricks = evalResult[2];
if (lastBuilding == buildingHeights.length - 1) {
return buildingHeights.length - 1;
}
if (remainingLadders > 0) {
maximum = mid - 1;
continue;
}
int nextRequiredClimb = buildingHeights[lastBuilding + 1] - buildingHeights[lastBuilding];
if (nextRequiredClimb > remainingBricks && mid > remainingBricks) {
return lastBuilding;
} else {
minimum = mid + 1;
}
}
return -1; // Control never reaches here as per conditions
}
public int[] evaluateThreshold(int[] heights, int bricks, int ladders, int threshold) {
int thresholdLadders = 0;
for (int i = 0; i < heights.length - 1; i++) {
int climb = heights[i + 1] - heights[i];
if (climb <= 0) {
continue;
}
if (climb == threshold) {
thresholdLadders++;
ladders--;
} else if (climb > threshold) {
ladders--;
} else {
bricks -= climb;
}
if (ladders < 0) {
if (thresholdLadders >= 1) {
thresholdLadders--;
ladders++;
bricks -= threshold;
} else {
return new int[]{i, ladders, bricks};
}
}
if (bricks < 0) {
return new int[]{i, ladders, bricks};
}
}
return new int[]{heights.length - 1, ladders, bricks};
}
}
The solution to "Furthest Building You Can Reach" focuses on determining the maximum index of an array representing building heights that can be reached using a specified number of bricks and ladders. The function maxReachableBuilding
takes an array buildingHeights
along with integers totalBricks
and totalLadders
as inputs and leverages a binary search to optimize the process of finding the furthest reachable building.
Here's an outline of how the solution works:
- Initialize parameters to track the minimum and maximum required climbs between consecutive buildings.
- Loop through the
buildingHeights
to calculate these minimum and maximum climb values. - Use a binary search between these two bounding values to find the optimal point (or threshold) where the combination of available bricks and ladders will allow the user to reach the furthest building:
- The midpoint, or threshold, in each iteration of the binary search represents a potential climb requirement where exactly
totalLadders
ladders will be utilized. - Based on this threshold, the
evaluateThreshold
function computes if the user can reach the last building with the given resources.
- The midpoint, or threshold, in each iteration of the binary search represents a potential climb requirement where exactly
- During each binary search iteration, decide whether to adjust the search bounds based on the remaining bricks and ladders after attempting to reach the furthest building using the current threshold.
The evaluateThreshold
function assists by:
- Iterating over building heights and applying either a ladder or bricks depending on whether the climb is above, equal to, or below the threshold.
- Adjusting the number of ladders and bricks accordingly and returning immediately if it’s impossible to proceed due to a shortage of either resource.
In essence, this solution efficiently examines feasible strategies for utilizing bricks and ladders by iteratively adjusting the threshold of required climbing effort and checking the resultant reachability using the allotted resources. This algorithm stops adjusting once it finds that further progress can't be made with the remaining resources, determining the furthest building that can be reached.
class BuildingClimber:
def maxReachableIndex(self, heights: List[int], bricks: int, ladders: int) -> int:
def check_threshold(climb_limit):
remaining_ladders = ladders
remaining_bricks = bricks
threshold_ladder_use = 0
for index in range(len(heights) - 1):
climb = heights[index + 1] - heights[index]
if climb <= 0:
continue
if climb == climb_limit:
threshold_ladder_use += 1
remaining_ladders -= 1
elif climb > climb_limit:
remaining_ladders -= 1
else:
remaining_bricks -= climb
if remaining_ladders < 0:
if threshold_ladder_use:
threshold_ladder_use -= 1
remaining_ladders += 1
remaining_bricks -= climb_limit
else:
return [index, remaining_ladders, remaining_bricks]
if remaining_bricks < 0:
return [index, remaining_ladders, remaining_bricks]
return [len(heights) - 1, remaining_ladders, remaining_bricks]
min_climb = math.inf
max_climb = -math.inf
for index in range(len(heights) - 1):
climb = heights[index + 1] - heights[index]
if climb <= 0:
continue
min_climb = min(min_climb, climb)
max_climb = max(max_climb, climb)
if min_climb == math.inf: # No upward climbs
return len(heights) - 1
while min_climb <= max_climb:
mid_climb = min_climb + (max_climb - min_climb) // 2
idx_reached, ladders_left, bricks_left = check_threshold(mid_climb)
if idx_reached == len(heights) - 1:
return len(heights) - 1
if ladders_left > 0:
max_climb = mid_climb - 1
continue
next_height_increase = heights[idx_reached + 1] - heights[idx_reached]
if bricks_left < next_height_increase and bricks_left < mid_climb:
return idx_reached
min_climb = mid_climb + 1
The provided solution implements a class BuildingClimber
with a method maxReachableIndex
to determine the furthest index in a list of building heights that can be reached given a certain number of bricks and ladders. The method uses the concept of binary search combined with a greedy approach to efficiently find the maximum reachable index. Here’s a concise explanation of the approach and key functionalities:
The function
check_threshold(climb_limit)
is a helper that simulates attempting to climb the buildings, using either bricks or ladders based on the height difference between consecutive buildings (climb
). It tracks and updates the count of remaining ladders and bricks as it progresses through the heights list.The main function first initializes
min_climb
to infinity andmax_climb
to negative infinity, then calculates the minimal and maximal climbs required between consecutive buildings. This sets the bounds for the binary search.If there are no positive climbs (
min_climb
is infinity), all buildings are either on the same level or only require descending, thus the last building can be reached directly.A binary search is conducted between
min_climb
andmax_climb
. For each middle pointmid_climb
, the helpercheck_threshold
is invoked to see if it is possible to reach the end using the current approach ofmid_climb
as a decision parameter for distributing ladders and bricks.Based on the output of this check:
- If the end of the list is reached, it indicates that it's possible to reach the last building with the current setup and hence returns the last index.
- If ladders or bricks are left over while the end isn't reached, adjustments to
min_climb
ormax_climb
are made accordingly to optimize the number used for further checks.
The termination of the search either confirms that the last index can be reached, or identifies the highest index that can be reached based on the available climbing aids.
The algorithm effectively balances between climbs that can or must be overcome using bricks versus ladders, ensuring an optimal solution to reach the furthest building within the given constraints.
No comments yet.