🧠
Binary Search on Speed (Answer Space)
💡 This approach uses binary search on the range of possible speeds to efficiently find the minimum speed that meets the time constraint. It introduces the concept of a feasibility function to check if a given speed is sufficient.
Intuition
Instead of checking every speed, use binary search between 1 and a large upper bound. For each mid speed, check if arrival time ≤ hour. Narrow the search space accordingly.
Algorithm
- Set low = 1 and high = 10^7 (upper bound).
- While low ≤ high:
- Calculate mid = (low + high) // 2.
- Check if speed = mid can get you there on time using feasibility function.
- If yes, update answer and move high to mid - 1.
- Else, move low to mid + 1.
- Return answer or -1 if no speed is feasible.
💡 The key is the feasibility check that simulates travel time at a given speed, including waiting times.
from math import ceil
def can_arrive(dist, hour, speed):
total_time = 0.0
for i in range(len(dist) - 1):
total_time += ceil(dist[i] / speed)
total_time += dist[-1] / speed
return total_time <= hour
def min_speed_on_time(dist, hour):
low, high = 1, 10**7
answer = -1
while low <= high:
mid = (low + high) // 2
if can_arrive(dist, hour, mid):
answer = mid
high = mid - 1
else:
low = mid + 1
return answer
# Driver code
if __name__ == '__main__':
print(min_speed_on_time([1,3,2], 6)) # Expected: 1
print(min_speed_on_time([1,3,2], 2.7)) # Expected: 3
print(min_speed_on_time([1,3,2], 1.9)) # Expected: -1
Line Notes
def can_arrive(dist, hour, speed):Defines feasibility function to check if speed meets time constraint
for i in range(len(dist) - 1):Calculate time for all but last segment with ceiling to handle waiting
total_time += dist[-1] / speedAdd exact time for last segment without waiting
if can_arrive(dist, hour, mid):Check feasibility at mid speed to adjust binary search bounds
import java.util.*;
public class MinSpeedOnTime {
private static boolean canArrive(int[] dist, double hour, int speed) {
double totalTime = 0.0;
for (int i = 0; i < dist.length - 1; i++) {
totalTime += Math.ceil((double) dist[i] / speed);
}
totalTime += (double) dist[dist.length - 1] / speed;
return totalTime <= hour;
}
public static int minSpeedOnTime(int[] dist, double hour) {
int low = 1, high = 10000000;
int answer = -1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (canArrive(dist, hour, mid)) {
answer = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return answer;
}
public static void main(String[] args) {
System.out.println(minSpeedOnTime(new int[]{1,3,2}, 6)); // 1
System.out.println(minSpeedOnTime(new int[]{1,3,2}, 2.7)); // 3
System.out.println(minSpeedOnTime(new int[]{1,3,2}, 1.9)); // -1
}
}
Line Notes
private static boolean canArrive(int[] dist, double hour, int speed)Feasibility function to check if speed is sufficient
for (int i = 0; i < dist.length - 1; i++)Calculate time for all but last segment with ceiling to handle waiting
totalTime += (double) dist[dist.length - 1] / speed;Add exact time for last segment without waiting
if (canArrive(dist, hour, mid))Use feasibility to adjust binary search bounds
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
bool canArrive(const vector<int>& dist, double hour, int speed) {
double totalTime = 0.0;
for (int i = 0; i < (int)dist.size() - 1; i++) {
totalTime += ceil((double)dist[i] / speed);
}
totalTime += (double)dist.back() / speed;
return totalTime <= hour;
}
int minSpeedOnTime(vector<int>& dist, double hour) {
int low = 1, high = 10000000;
int answer = -1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (canArrive(dist, hour, mid)) {
answer = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return answer;
}
int main() {
vector<int> dist1 = {1,3,2};
cout << minSpeedOnTime(dist1, 6) << "\n"; // 1
cout << minSpeedOnTime(dist1, 2.7) << "\n"; // 3
cout << minSpeedOnTime(dist1, 1.9) << "\n"; // -1
return 0;
}
Line Notes
bool canArrive(const vector<int>& dist, double hour, int speed)Feasibility function to check if speed suffices
for (int i = 0; i < (int)dist.size() - 1; i++)Calculate time for all but last segment with ceiling to handle waiting
totalTime += (double)dist.back() / speed;Add exact time for last segment without waiting
if (canArrive(dist, hour, mid))Adjust binary search bounds based on feasibility
function canArrive(dist, hour, speed) {
let totalTime = 0;
for (let i = 0; i < dist.length - 1; i++) {
totalTime += Math.ceil(dist[i] / speed);
}
totalTime += dist[dist.length - 1] / speed;
return totalTime <= hour;
}
function minSpeedOnTime(dist, hour) {
let low = 1, high = 10000000;
let answer = -1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
if (canArrive(dist, hour, mid)) {
answer = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return answer;
}
// Test cases
console.log(minSpeedOnTime([1,3,2], 6)); // 1
console.log(minSpeedOnTime([1,3,2], 2.7)); // 3
console.log(minSpeedOnTime([1,3,2], 1.9)); // -1
Line Notes
function canArrive(dist, hour, speed)Feasibility function to check if speed meets time constraint
for (let i = 0; i < dist.length - 1; i++)Calculate time for all but last segment with ceiling to handle waiting
totalTime += dist[dist.length - 1] / speed;Add exact time for last segment without waiting
if (canArrive(dist, hour, mid))Adjust binary search bounds based on feasibility
TimeO(n log(maxSpeed))
SpaceO(1)
Binary search runs in log(maxSpeed) iterations, each checking n segments for feasibility.
💡 For n=10^5 and maxSpeed=10^7, this means about 24 * 10^5 = 2.4 million operations, which is efficient enough.
Interview Verdict: Accepted
This approach is efficient and practical for large inputs, making it the preferred solution in interviews.