Maximum Earnings From Taxi — Medium Problem & Solution
You drive a taxi along a road with points 1 to n, always moving from point 1 towards point n — you can never turn back.
- Difficulty: Medium
- Topics: Arrays, Dynamic Programming, Hash Table, Sorting
- Asked at: Amazon, Uber
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
You drive a taxi along a road with points 1 to n, always moving from point 1 towards point n — you can never turn back.
rides[i] = [start_i, end_i, tip_i] is a passenger who wants to go from start_i to end_i and tips tip_i. Carrying that passenger earns end_i - start_i + tip_i. You can carry at most one passenger at a time, but you may pick someone up at the very point where you drop the previous passenger off.
Return the maximum amount you can earn.
CodeKairo bound: tips are capped at 10^4 (LeetCode allows 10^5) so that the answer always fits in a 32-bit integer.
Example 1
Input: n = 5, rides = [[2,5,4],[1,5,1]]
Output: 7
Explanation: Take the first passenger: 5 - 2 + 4.
Example 2
Input: n = 10, rides = [[1,4,2],[4,7,1],[2,9,3]]
Output: 10
Explanation: The two short rides earn 5 + 4 = 9; the long one alone earns 7 + 3 = 10.
Example 3
Input: n = 20, rides = [[1,6,1],[3,10,2],[10,12,3],[11,12,2],[12,15,2],[13,18,1]]
Output: 20
Constraints
1 <= n <= 10^51 <= rides.length <= 3 * 10^4rides[i].length == 31 <= start_i < end_i <= n1 <= tip_i <= 10^4
How to solve Maximum Earnings From Taxi
Sweep the road left to right. The best earnings at point p either carry over from p - 1 or come from finishing some ride exactly at p, which started at a point whose best earnings are already known.
Approach
- Bucket (or sort) the rides by end point.
dp[1] = 0. Forp = 2..n:dp[p] = dp[p - 1], then for each ride[s, p, tip]ending here,dp[p] = max(dp[p], dp[s] + p - s + tip).- Return
dp[n].
Why it works
Any valid schedule is a chain of non-overlapping rides in order of position. The last ride finishing at or before p either ends exactly at p (and the rest of the schedule is a valid schedule up to its start s, worth at most dp[s]) or ends earlier (covered by dp[p - 1]). Induction on p shows dp[p] is the true optimum.
Complexity
- Time —
O(n + r log r) - Space —
O(n + r)
Pitfalls
- A new ride may start at the same point the previous one ends — compare with
<=, not<. - The base fare
end - startcounts too, not just the tip. - With LeetCode's original bounds the answer needs 64 bits; the tightened tip bound keeps it under 2^31.
Reference solution
Python
from typing import List
def maxTaxiEarnings(n: int, rides: List[List[int]]) -> int:
ending = [[] for _ in range(n + 1)]
for s, e, tip in rides:
ending[e].append((s, e - s + tip))
dp = [0] * (n + 1)
for p in range(2, n + 1):
best = dp[p - 1]
for s, gain in ending[p]:
if dp[s] + gain > best:
best = dp[s] + gain
dp[p] = best
return dp[n]JavaScript
var maxTaxiEarnings = function(n, rides) {
var rs = rides.slice().sort(function(a, b) { return a[1] - b[1]; });
var dp = new Array(n + 1).fill(0);
var ptr = 0;
for (var p = 1; p <= n; p++) {
if (p > 1) dp[p] = dp[p - 1];
while (ptr < rs.length && rs[ptr][1] === p) {
var r = rs[ptr];
var cand = dp[r[0]] + r[1] - r[0] + r[2];
if (cand > dp[p]) dp[p] = cand;
ptr++;
}
}
return dp[n];
};Also on the editorial tab: C, C#, C++, Go, Java, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 988 arrays problems · the whole catalogue
Learn the technique: Arrays · Dynamic Programming