774. Minimize Max Distance to Gas Station 🔒
Problem Description
You have gas stations positioned along a straight line (x-axis), represented by an integer array stations
where each element indicates the position of a gas station.
Your task is to add exactly k
new gas stations. These new stations can be placed at any position on the x-axis, including non-integer positions (like 3.5 or 7.25).
After adding all k
new stations, you need to minimize the "penalty", which is defined as the maximum distance between any two adjacent gas stations (including both original and newly added stations).
For example, if you have stations at positions [1, 5, 10]
and the distances between adjacent stations are 4 (between 1 and 5) and 5 (between 5 and 10), the penalty would be 5 (the maximum distance). By adding new stations strategically, you can reduce this maximum distance.
The goal is to find the optimal placement of k
new stations such that the penalty (maximum distance between adjacent stations) is as small as possible. Return this minimum possible penalty value.
The answer will be accepted if it's within 10^-6
of the actual answer, allowing for floating-point precision tolerance.
Intuition
The key insight is recognizing that this is an optimization problem where we want to minimize the maximum value - a classic scenario for binary search on the answer.
Think about it this way: if someone tells you "the maximum distance between adjacent stations must be at most x
", you can calculate exactly how many new stations you'd need to achieve this. For each existing gap between stations, if the gap is larger than x
, you'll need to add stations to break it down into smaller segments of size at most x
.
For a gap of size d
, the number of new stations needed is floor(d / x)
. Why? Because we're essentially dividing the gap into segments of size x
, and the number of divisions tells us how many stations to insert.
This creates a monotonic relationship: as x
decreases (we want smaller maximum distance), the number of required stations increases. Conversely, as x
increases (we allow larger maximum distance), fewer stations are needed.
Since we have exactly k
stations to add, we're looking for the smallest value of x
such that we need at most k
stations to ensure no gap exceeds x
. This is perfect for binary search:
- If a certain maximum distance
x
requires more thank
stations, thenx
is too small - If it requires
k
or fewer stations, thenx
is feasible and we can try an even smaller value
The search space is bounded between 0 (theoretical minimum) and the largest existing gap (or some large value like 10^8
). We keep narrowing down this range until we converge on the optimal maximum distance, using a precision threshold of 10^-6
to determine when to stop.
Learn more about Binary Search patterns.
Solution Approach
The solution implements binary search on the answer space to find the minimum possible maximum distance between adjacent gas stations.
Implementation Details:
-
Binary Search Setup:
- Initialize
left = 0
(minimum possible distance) andright = 1e8
(a sufficiently large upper bound for maximum distance) - Continue searching while
right - left > 1e-6
to achieve the required precision
- Initialize
-
Check Function:
- The
check(x)
function determines if we can achieve a maximum distance ofx
with at mostk
new stations - For each pair of adjacent stations
(a, b)
in the original array, calculate how many new stations are needed in that gap - The formula
int((b - a) / x)
computes the number of stations needed:(b - a)
is the gap size- Dividing by
x
tells us how many segments of sizex
we need - Using
int()
gives us the floor value, which represents the number of cuts (new stations) needed
- Sum all required stations across all gaps and check if it's
<= k
- The
-
Binary Search Logic:
- Calculate
mid = (left + right) / 2
- If
check(mid)
returnsTrue
(we can achieve distancemid
withk
or fewer stations):- Update
right = mid
to try for an even smaller maximum distance
- Update
- Otherwise (we need more than
k
stations):- Update
left = mid
becausemid
is too small to be achievable
- Update
- Calculate
-
Using
pairwise
:- The
pairwise(stations)
function generates consecutive pairs from the stations array - For example,
[1, 5, 10]
produces pairs(1, 5)
and(5, 10)
- This elegantly iterates through all adjacent gaps in the original stations
- The
-
Return Value:
- After convergence, return
left
which represents the minimum achievable maximum distance - Both
left
andright
will have converged to approximately the same value within the precision threshold
- After convergence, return
The time complexity is O(n * log(W/ε))
where n
is the number of stations, W
is the search range, and ε
is the precision (1e-6
). The space complexity is O(1)
as we only use a constant amount of extra space.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with stations = [1, 7, 15]
and k = 2
new stations to add.
Initial Setup:
- Original stations:
[1, 7, 15]
- Gaps between stations:
7-1=6
and15-7=8
- Current penalty (max gap):
8
- We need to add exactly
2
new stations to minimize this penalty
Binary Search Process:
Round 1:
left = 0
,right = 100
(using 100 as upper bound for simplicity)mid = 50
- Check if we can achieve max distance of
50
with2
stations:- Gap
[1, 7]
: size6
, needsint(6/50) = 0
stations - Gap
[7, 15]
: size8
, needsint(8/50) = 0
stations - Total needed:
0
stations ≤2
✓
- Gap
- Since feasible, try smaller:
right = 50
Round 2:
left = 0
,right = 50
mid = 25
- Check if we can achieve max distance of
25
:- Gap
[1, 7]
: needsint(6/25) = 0
stations - Gap
[7, 15]
: needsint(8/25) = 0
stations - Total:
0
stations ≤2
✓
- Gap
- Try smaller:
right = 25
Round 3:
left = 0
,right = 25
mid = 12.5
- Check if we can achieve max distance of
12.5
:- Gap
[1, 7]
: needsint(6/12.5) = 0
stations - Gap
[7, 15]
: needsint(8/12.5) = 0
stations - Total:
0
stations ≤2
✓
- Gap
- Try smaller:
right = 12.5
Round 4:
left = 0
,right = 12.5
mid = 6.25
- Check if we can achieve max distance of
6.25
:- Gap
[1, 7]
: needsint(6/6.25) = 0
stations - Gap
[7, 15]
: needsint(8/6.25) = 1
station - Total:
1
station ≤2
✓
- Gap
- Try smaller:
right = 6.25
Round 5:
left = 0
,right = 6.25
mid = 3.125
- Check if we can achieve max distance of
3.125
:- Gap
[1, 7]
: needsint(6/3.125) = 1
station - Gap
[7, 15]
: needsint(8/3.125) = 2
stations - Total:
3
stations >2
✗
- Gap
- Too ambitious, increase:
left = 3.125
Round 6:
left = 3.125
,right = 6.25
mid = 4.6875
- Check if we can achieve max distance of
4.6875
:- Gap
[1, 7]
: needsint(6/4.6875) = 1
station - Gap
[7, 15]
: needsint(8/4.6875) = 1
station - Total:
2
stations =2
✓
- Gap
- Try smaller:
right = 4.6875
Continuing...
The binary search continues, narrowing the range between left
and right
until they converge within 1e-6
.
Final Result:
The algorithm converges to approximately 4.0
. With this maximum distance:
- We place 1 station in gap
[1, 7]
at position4.5
→ gaps become[1, 4.5]
(size 3.5) and[4.5, 7]
(size 2.5) - We place 1 station in gap
[7, 15]
at position11
→ gaps become[7, 11]
(size 4) and[11, 15]
(size 4) - Final stations:
[1, 4.5, 7, 11, 15]
- All gaps:
3.5, 2.5, 4, 4
- Maximum gap (penalty):
4.0
This demonstrates how binary search efficiently finds the optimal placement strategy by testing different maximum distance thresholds and checking feasibility.
Solution Implementation
1class Solution:
2 def minmaxGasDist(self, stations: List[int], k: int) -> float:
3 """
4 Find the minimum possible maximum distance between adjacent gas stations
5 after adding k new stations optimally.
6
7 Args:
8 stations: List of positions of existing gas stations (sorted)
9 k: Number of new gas stations to add
10
11 Returns:
12 Minimum possible maximum distance between adjacent stations
13 """
14
15 def can_achieve_max_distance(max_dist: float) -> bool:
16 """
17 Check if we can achieve a maximum distance of max_dist between
18 adjacent stations by adding at most k new stations.
19
20 For each gap between existing stations, calculate how many new
21 stations we need to ensure no gap exceeds max_dist.
22 """
23 total_stations_needed = 0
24
25 # Check each consecutive pair of existing stations
26 for i in range(len(stations) - 1):
27 gap = stations[i + 1] - stations[i]
28 # Calculate stations needed to make this gap <= max_dist
29 # Using int() for floor division of the gap by max_dist
30 stations_needed = int(gap / max_dist)
31 total_stations_needed += stations_needed
32
33 # Return true if we can achieve this with k or fewer stations
34 return total_stations_needed <= k
35
36 # Binary search on the answer (maximum distance)
37 # Left bound: 0 (minimum possible distance)
38 # Right bound: 1e8 (large enough for any reasonable input)
39 left, right = 0.0, 1e8
40
41 # Continue binary search until precision is sufficient (1e-6)
42 while right - left > 1e-6:
43 mid = (left + right) / 2
44
45 # If we can achieve max distance of mid with k stations
46 if can_achieve_max_distance(mid):
47 # Try to find a smaller maximum distance
48 right = mid
49 else:
50 # Need a larger maximum distance
51 left = mid
52
53 return left
54
1class Solution {
2 /**
3 * Minimizes the maximum distance between adjacent gas stations after adding k new stations.
4 * Uses binary search on the answer to find the minimum possible maximum distance.
5 *
6 * @param stations Array of existing gas station positions on x-axis
7 * @param k Number of new gas stations to add
8 * @return Minimum possible maximum distance between adjacent stations
9 */
10 public double minmaxGasDist(int[] stations, int k) {
11 // Binary search bounds: left = 0, right = 1e8 (large enough upper bound)
12 double left = 0;
13 double right = 1e8;
14
15 // Binary search with precision of 1e-6
16 while (right - left > 1e-6) {
17 double mid = (left + right) / 2.0;
18
19 // Check if we can achieve maximum distance of 'mid' with k stations
20 if (canAchieveMaxDistance(mid, stations, k)) {
21 // If possible, try to find a smaller maximum distance
22 right = mid;
23 } else {
24 // If not possible, we need a larger maximum distance
25 left = mid;
26 }
27 }
28
29 return left;
30 }
31
32 /**
33 * Checks if it's possible to add stations such that maximum distance is at most maxDistance.
34 *
35 * @param maxDistance Target maximum distance between adjacent stations
36 * @param stations Array of existing gas station positions
37 * @param k Number of available new stations
38 * @return true if achievable with k or fewer stations, false otherwise
39 */
40 private boolean canAchieveMaxDistance(double maxDistance, int[] stations, int k) {
41 int requiredStations = 0;
42
43 // Calculate minimum stations needed between each pair of existing stations
44 for (int i = 0; i < stations.length - 1; i++) {
45 // Distance between current and next station
46 double distance = stations[i + 1] - stations[i];
47
48 // Number of new stations needed to ensure max gap is at most maxDistance
49 // Using floor division to get minimum stations needed
50 requiredStations += (int) (distance / maxDistance);
51 }
52
53 // Check if required stations is within our budget
54 return requiredStations <= k;
55 }
56}
57
1class Solution {
2public:
3 double minmaxGasDist(vector<int>& stations, int k) {
4 // Binary search bounds: minimum and maximum possible distances
5 double left = 0.0;
6 double right = 1e8;
7
8 // Lambda function to check if a given max distance is achievable
9 // with at most k additional stations
10 auto canAchieveMaxDistance = [&](double maxDistance) {
11 int requiredStations = 0;
12
13 // Calculate how many stations needed between each pair of existing stations
14 for (int i = 0; i < stations.size() - 1; ++i) {
15 double gap = stations[i + 1] - stations[i];
16 // Number of stations needed to ensure no gap exceeds maxDistance
17 requiredStations += static_cast<int>(gap / maxDistance);
18 }
19
20 // Check if we can achieve this with k or fewer stations
21 return requiredStations <= k;
22 };
23
24 // Binary search with precision of 1e-6
25 while (right - left > 1e-6) {
26 double mid = (left + right) / 2.0;
27
28 // If we can achieve mid as max distance, try smaller values
29 if (canAchieveMaxDistance(mid)) {
30 right = mid;
31 }
32 // Otherwise, we need a larger max distance
33 else {
34 left = mid;
35 }
36 }
37
38 // Return the minimum achievable maximum distance
39 return left;
40 }
41};
42
1function minmaxGasDist(stations: number[], k: number): number {
2 // Binary search bounds: minimum and maximum possible distances
3 let left: number = 0.0;
4 let right: number = 1e8;
5
6 // Helper function to check if a given max distance is achievable
7 // with at most k additional stations
8 const canAchieveMaxDistance = (maxDistance: number): boolean => {
9 let requiredStations: number = 0;
10
11 // Calculate how many stations needed between each pair of existing stations
12 for (let i = 0; i < stations.length - 1; i++) {
13 const gap: number = stations[i + 1] - stations[i];
14 // Number of stations needed to ensure no gap exceeds maxDistance
15 // Using Math.floor to get integer division result
16 requiredStations += Math.floor(gap / maxDistance);
17 }
18
19 // Check if we can achieve this with k or fewer stations
20 return requiredStations <= k;
21 };
22
23 // Binary search with precision of 1e-6
24 while (right - left > 1e-6) {
25 const mid: number = (left + right) / 2.0;
26
27 // If we can achieve mid as max distance, try smaller values
28 if (canAchieveMaxDistance(mid)) {
29 right = mid;
30 }
31 // Otherwise, we need a larger max distance
32 else {
33 left = mid;
34 }
35 }
36
37 // Return the minimum achievable maximum distance
38 return left;
39}
40
Time and Space Complexity
Time Complexity: O(n * log(M/ε))
where n
is the number of stations, M
is the maximum distance between consecutive stations (bounded by 1e8
), and ε
is the precision (1e-6
).
- The binary search runs for
log((right - left) / ε) = log(1e8 / 1e-6) = log(1e14)
iterations, which is approximately 47 iterations. - In each iteration of the binary search, the
check
function is called. - The
check
function iterates through all consecutive pairs of stations usingpairwise(stations)
, which takesO(n-1) = O(n)
time. - For each pair, it performs a constant time operation:
int((b - a) / x)
. - Therefore, each
check
call takesO(n)
time. - Total time complexity:
O(n * log(M/ε))
Space Complexity: O(1)
or O(n)
depending on the implementation of pairwise
.
- The binary search uses only a constant amount of extra space for variables
left
,right
, andmid
. - The
check
function uses a generator expression withsum()
, which doesn't create an intermediate list. - However,
pairwise(stations)
may create an iterator that could useO(1)
space if implemented efficiently, orO(n)
space if it creates intermediate pairs. - In Python 3.10+,
itertools.pairwise
usesO(1)
extra space. - Overall space complexity:
O(1)
auxiliary space (excluding input).
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Station Counting Formula
Pitfall: Using ceil((b - a) / x)
or (b - a) // x + 1
to count the number of new stations needed in a gap.
When you have a gap of size d
and want segments of maximum size x
, many developers incorrectly think they need ceil(d/x)
new stations. However, this counts the number of segments, not the number of dividing points (stations).
Example:
- Gap between stations: 10 units (stations at position 0 and 10)
- Maximum allowed distance: 5 units
- Incorrect calculation:
ceil(10/5) = 2
stations - Correct calculation:
int(10/5) = 2
stations
Both give 2 here, but consider:
- Gap: 10 units, max distance: 3 units
- Incorrect:
ceil(10/3) = 4
stations (creates 5 segments - too many!) - Correct:
int(10/3) = 3
stations (creates 4 segments of sizes ≤3)
Solution: Use int(gap / max_dist)
which correctly calculates the number of cuts (new stations) needed to ensure no segment exceeds max_dist
.
2. Binary Search Boundary Issues
Pitfall: Setting the right boundary too small, like using the maximum gap in the original array as the upper bound.
# Incorrect:
right = max(stations[i+1] - stations[i] for i in range(len(stations)-1))
Problem: When k=0 (no new stations to add), this would fail because the answer IS the maximum gap, and binary search needs room to converge properly with floating-point precision.
Solution: Use a sufficiently large upper bound like 1e8
or explicitly set it to the maximum gap when k>0:
# Correct approach:
if k == 0:
return float(max(stations[i+1] - stations[i] for i in range(len(stations)-1)))
else:
right = max(stations[i+1] - stations[i] for i in range(len(stations)-1))
# or simply use a large constant like 1e8
3. Floating-Point Precision Errors
Pitfall: Using exact equality or insufficient precision in the termination condition.
# Incorrect: while left < right: # May loop forever with floating-point numbers mid = (left + right) / 2 ...
Solution: Always use a precision threshold:
# Correct: while right - left > 1e-6: mid = (left + right) / 2 ...
4. Off-by-One Error in Station Counting
Pitfall: Confusing the relationship between gaps, segments, and dividing points.
Remember:
- Number of gaps = n - 1 (for n original stations)
- Number of new stations in a gap = number of cuts needed
- Number of segments after k cuts = k + 1
Solution: Visualize with small examples and verify the formula with edge cases like when the gap is exactly divisible by the maximum distance.
How would you design a stack which has a function min
that returns the minimum element in the stack, in addition to push
and pop
? All push
, pop
, min
should have running time O(1)
.
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!