Minimum Time to Finish the Race solution and explanation
6 more videos on this problem
Minimum Time to Finish the Race | Leetcode 2188 | Contest 282 | DP | Knapsack problem 🔥🔥Coding Decoded · 11:02 · 3K views
2188. Minimum Time to Finish the Race || LeetCode 2188 || LeetCode Weekly Contest 282Bro Coders · 26:31 · 1K views
Leetcode Weekly 282 | 4th Problem || 2188. Minimum Time to Finish the RacecodingMohan · 18:25 · 975 views
【每日一题】LeetCode 2188. Minimum Time to Finish the RaceHuifeng Guan · 38:04 · 868 views
2188. Minimum Time to Finish the Race (Leetcode Hard)Programming Live with Larry · 49:35 · 645 views
Minimum Time to Finish the RaceNot A Boring Developer · 7:45 · 583 views
LeetCode 2188: Minimum Time to Finish the RaceAlitaCode · 33:24 · 12 views
The problem
You are given a 0-indexed 2D integer array tires where tires[i] = [fi, ri] indicates that the ith tire can finish its xth successive lap in fi * ri(x-1) seconds.
- For example, if
fi = 3andri = 2, then the tire would finish its1stlap in3seconds, its2ndlap in3 * 2 = 6seconds, its3rdlap in3 * 22 = 12seconds, etc.
You are also given an integer changeTime and an integer numLaps.
The race consists of numLaps laps and you may start the race with any tire. You have an unlimited supply of each tire and after every lap, you may change to any given tire (including the current tire type) if you wait changeTime seconds.
Return the minimum time to finish the race.
Example 1:
Input: tires = [[2,3],[3,4]], changeTime = 5, numLaps = 4 Output: 21 Explanation: Lap 1: Start with tire 0 and finish the lap in 2 seconds. Lap 2: Continue with tire 0 and finish the lap in 2 * 3 = 6 seconds. Lap 3: Change tires to a new tire 0 for 5 seconds and then finish the lap in another 2 seconds. Lap 4: Continue with tire 0 and finish the lap in 2 * 3 = 6 seconds. Total time = 2 + 6 + 5 + 2 + 6 = 21 seconds. The minimum time to complete the race is 21 seconds.
Example 2:
Input: tires = [[1,10],[2,2],[3,4]], changeTime = 6, numLaps = 5 Output: 25 Explanation: Lap 1: Start with tire 1 and finish the lap in 2 seconds. Lap 2: Continue with tire 1 and finish the lap in 2 * 2 = 4 seconds. Lap 3: Change tires to a new tire 1 for 6 seconds and then finish the lap in another 2 seconds. Lap 4: Continue with tire 1 and finish the lap in 2 * 2 = 4 seconds. Lap 5: Change tires to tire 0 for 6 seconds then finish the lap in another 1 second. Total time = 2 + 4 + 6 + 2 + 4 + 6 + 1 = 25 seconds. The minimum time to complete the race is 25 seconds.
Constraints:
1 <= tires.length <= 105tires[i].length == 21 <= fi, changeTime <= 1052 <= ri <= 1051 <= numLaps <= 1000
Approach overview
Problem Overview: You have multiple tire types where each lap gets slower due to degradation. After any lap you can change tires with a fixed changeTime. The goal is to finish numLaps with the minimum total time by deciding when to continue with the same tire or perform a tire change.
Approach 1: Greedy + Brute Force for Small Laps (O(t * n^2) time, O(n) space)
The naive idea is to simulate how long a tire performs if you keep using it for consecutive laps. For each tire with base time f and degradation factor r, lap times grow as f, f*r, f*r^2.... If the next lap becomes slower than starting fresh after a tire change (changeTime + f), continuing is pointless. Precompute cumulative times for every tire while it remains beneficial. Then try building the race lap-by-lap using brute force: for every lap count i, test all possible previous segments of consecutive laps. This approach directly models the decision process but becomes expensive when numLaps grows large.
Approach 2: Dynamic Programming with Precomputation (O(t * k + n * k) time, O(n) space)
The key observation: no tire is useful for many consecutive laps because degradation grows exponentially. For each tire, simulate consecutive laps until the next lap exceeds changeTime + f. Track the best possible time to complete exactly k consecutive laps without a tire change. Because growth is exponential, k stays small (usually ≤ 18).
Once these values are precomputed, the problem becomes classic dynamic programming. Let dp[i] be the minimum time to finish i laps. For every possible consecutive segment length k, update:
dp[i] = min(dp[i], dp[i-k] + best[k] + changeTime)
The extra changeTime represents switching tires before the new segment. Handle the first segment separately so it does not pay the initial change cost. Precomputation limits the inner loop to a small constant, turning a potentially quadratic search into a near linear solution.
This method combines insights from array processing and dynamic programming. Instead of exploring every tire choice at every lap, it compresses all tire behavior into the best possible segment times.
Recommended for interviews: Dynamic Programming with precomputation. Interviewers expect you to notice that tire degradation grows exponentially, which bounds the number of useful consecutive laps. Demonstrating the brute-force idea shows understanding of the decision space, but the optimized DP proves you can reduce the search space and build an efficient solution.
Approach 1: Dynamic Programming with Precomputation
In this approach, we precompute the minimum time required to finish 'k' consecutive laps using each tire without changing and use dynamic programming to find the minimum race time by choosing between continuing with the current tire or changing to another tire.
def minTimeToFinishRace(tires, changeTime, numLaps):
max_laps = 20
min_time_for_laps = [float('inf')] * (max_laps + 1)
for f, r in tires:
curr_time = 0
curr_factor = 1
for lap in range(1, max_laps + 1):
curr_time += f * curr_factor
if curr_time > changeTime + f:
break
min_time_for_laps[lap] = min(min_time_for_laps[lap], curr_time)
curr_factor *= r
dp = [float('inf')] * (numLaps + 1)
dp[0] = 0
for laps in range(1, numLaps + 1):
for k in range(1, min(laps, max_laps) + 1):
dp[laps] = min(dp[laps], dp[laps - k] + min_time_for_laps[k] + (0 if laps == k else changeTime))
return dp[numLaps]public class Solution {
public int minTimeToFinishRace(int[][] tires, int changeTime, int numLaps) {
int maxLaps = 20;
int[] minTimeForLaps = new int[maxLaps + 1];
Arrays.fill(minTimeForLaps, Integer.MAX_VALUE);
for (int[] tire : tires) {
int f = tire[0], r = tire[1];
int currentTime = 0, currentFactor = 1;
for (int lap = 1; lap <= maxLaps; lap++) {
currentTime += f * currentFactor;
if (currentTime > changeTime + f) break;
minTimeForLaps[lap] = Math.min(minTimeForLaps[lap], currentTime);
currentFactor *= r;
}
}
int[] dp = new int[numLaps + 1];
Arrays.fill(dp, Integer.MAX_VALUE);
dp[0] = 0;
for (int laps = 1; laps <= numLaps; laps++) {
for (int k = 1; k <= Math.min(laps, maxLaps); k++) {
dp[laps] = Math.min(dp[laps], dp[laps - k] + minTimeForLaps[k] + (laps == k ? 0 : changeTime));
}
}
return dp[numLaps];
}
}The function begins by initializing variables for precomputation (max_laps) and dynamic programming (dp array). It iterates through the available tires and calculates the minimum time for each possible number of consecutive laps up to a predefined maximum.
Then, using dynamic programming, it calculates the minimum time required for every possible number of laps, considering both continuing with the same tire and changing tires. The solution for all laps is found at the last position of the dp array.
Time Complexity: O(n * max_laps + numLaps * max_laps), where n is the number of tires and max_laps is the predefined maximum number of laps calculated by a single tire.
Space Complexity: O(numLaps), since we store the minimum time for each lap up to numLaps.
Approach 2: Greedy + Brute Force for Small Laps
For smaller values of numLaps, a greedy approach combined with brute force might be effective. Check each tire's effectiveness over small contiguous sequences of laps to maximize efficiency. If efficient, use it greedily; otherwise, brute force the sequences.
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int minTimeToFinishRace(vector<vector<int>>& tires, int changeTime, int numLaps) {
int maxLaps = 20;
vector<int> minTimeForLaps(maxLaps + 1, INT_MAX);
for (auto& tire : tires) {
int f = tire[0], r = tire[1];
int currentTime = 0, currentFactor = 1;
for (int lap = 1; lap <= maxLaps; lap++) {
currentTime += f * currentFactor;
if (currentTime > changeTime + f) break;
minTimeForLaps[lap] = min(minTimeForLaps[lap], currentTime);
currentFactor *= r;
}
}
vector<int> dp(numLaps + 1, INT_MAX);
dp[0] = 0;
for (int laps = 1; laps <= numLaps; laps++) {
for (int k = 1; k <= min(laps, maxLaps); k++) {
dp[laps] = min(dp[laps], dp[laps - k] + minTimeForLaps[k] + (laps == k ? 0 : changeTime));
}
}
return dp[numLaps];
}function minTimeToFinishRace(tires, changeTime, numLaps) {
const maxLaps = 20;
const minTimeForLaps = new Array(maxLaps + 1).fill(Infinity);
for (const [f, r] of tires) {
let currentTime = 0, currentFactor = 1;
for (let lap = 1; lap <= maxLaps; lap++) {
currentTime += f * currentFactor;
if (currentTime > changeTime + f) break;
minTimeForLaps[lap] = Math.min(minTimeForLaps[lap], currentTime);
currentFactor *= r;
}
}
const dp = new Array(numLaps + 1).fill(Infinity);
dp[0] = 0;
for (let laps = 1; laps <= numLaps; laps++) {
for (let k = 1; k <= Math.min(laps, maxLaps); k++) {
dp[laps] = Math.min(dp[laps], dp[laps - k] + minTimeForLaps[k] + (laps === k ? 0 : changeTime));
}
}
return dp[numLaps];
}This C++ solution similarly precomputes consecutive lap times before applying dynamic programming to find the most efficient combination of tire with changes over the race distance, embracing both greedy and brute force characteristics for potential use cases like minimized changeTime.
Time Complexity: O(n * max_laps + numLaps * max_laps).
Space Complexity: O(numLaps).
Approach 3: Default Approach
function minimumFinishTime(tires: number[][], changeTime: number, numLaps: number): number {
const cost: number[] = Array(18).fill(Infinity);
for (const [f, r] of tires) {
let s = 0;
let t = f;
for (let i = 1; t <= changeTime + f; ++i) {
s += t;
cost[i] = Math.min(cost[i], s);
t *= r;
}
}
const f: number[] = Array(numLaps + 1).fill(Infinity);
f[0] = -changeTime;
for (let i = 1; i <= numLaps; ++i) {
for (let j = 1; j < Math.min(18, i + 1); ++j) {
f[i] = Math.min(f[i], f[i - j] + cost[j]);
}
f[i] += changeTime;
}
return f[numLaps];
}func minimumFinishTime(tires [][]int, changeTime int, numLaps int) int {
const inf = 1 << 30
cost := [18]int{}
for i := range cost {
cost[i] = inf
}
for _, e := range tires {
f, r := e[0], e[1]
s, t := 0, f
for i := 1; t <= changeTime+f; i++ {
s += t
cost[i] = min(cost[i], s)
t *= r
}
}
f := make([]int, numLaps+1)
for i := range f {
f[i] = inf
}
f[0] = -changeTime
for i := 1; i <= numLaps; i++ {
for j := 1; j < min(18, i+1); j++ {
f[i] = min(f[i], f[i-j]+cost[j])
}
f[i] += changeTime
}
return f[numLaps]
}Complexity comparison
| Approach | Complexity |
|---|---|
| Dynamic Programming with Precomputation | Time Complexity: O(n * max_laps + numLaps * max_laps), where n is the number of tires and max_laps is the predefined maximum number of laps calculated by a single tire. Space Complexity: O(numLaps), since we store the minimum time for each lap up to numLaps. |
| Greedy + Brute Force for Small Laps | Time Complexity: O(n * max_laps + numLaps * max_laps). Space Complexity: O(numLaps). |
| Default Approach | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Greedy + Brute Force for Small Laps | O(t * n^2) | O(n) | Useful for understanding the decision process or when numLaps is very small |
| Dynamic Programming with Precomputation | O(t * k + n * k) | O(n) | Optimal approach for large numLaps where tire degradation limits consecutive usage |
Frequently asked questions
Is Minimum Time to Finish the Race easy or hard?
Minimum Time to Finish the Race is classified as a Hard problem. The challenge comes from recognizing that exponential tire degradation limits the number of useful consecutive laps and converting the problem into a dynamic programming formulation with preprocessing.
Minimum Time to Finish the Race Python/Java solution
Python and Java implementations typically follow the same pattern: precompute the best time for k consecutive laps for every tire, keep the minimum for each k, then run dynamic programming over the lap count. The DP transitions combine previous lap states with the precomputed segment times.
How to solve Minimum Time to Finish the Race in O(n)?
The near-linear solution uses dynamic programming with precomputation. First compute the best time to run k consecutive laps without changing tires. Then fill a DP array where dp[i] = min(dp[i-k] + best[k] + changeTime). Because k is bounded by exponential tire degradation, the inner loop is constant-sized, making the algorithm effectively O(n).
What is the best approach for Minimum Time to Finish the Race?
Dynamic programming with tire precomputation is the most efficient approach. First compute the minimum time to run k consecutive laps with any tire before degradation becomes worse than changing tires. Then use DP where dp[i] represents the minimum time to finish i laps. This reduces the complexity to roughly O(t*k + n*k) where k is small (usually under 20).
Is Minimum Time to Finish the Race asked at Google/Amazon/Meta?
Hard dynamic programming problems involving optimization and state transitions are commonly asked at companies like Google, Amazon, and Meta. This problem tests recognition of exponential growth limits, preprocessing, and DP state transitions—skills frequently evaluated in senior-level interviews.
What data structure is used in Minimum Time to Finish the Race?
The main structure is a dynamic programming array where dp[i] stores the minimum time to finish i laps. Additional arrays store precomputed best times for consecutive laps with a single tire. The solution primarily relies on arrays and dynamic programming rather than complex data structures.
What is the time complexity of Minimum Time to Finish the Race?
The optimal solution runs in O(t*k + n*k) time where t is the number of tires, n is the number of laps, and k is the maximum useful consecutive laps before degradation becomes too large. Since lap times grow exponentially, k stays small (around 18). Space complexity is O(n) for the DP array.
