Maximum Profit in Job Scheduling — Problem & Solution
Maximum Profit in Job Scheduling solution explained: step-by-step approach, complexity and code in Python, Java, C++ and JavaScript. A hard problem.
Maximum Profit in Job Scheduling is a hard coding problem on arrays, dynamic programming and sorting, asked in Amazon, Google, Microsoft and Airbnb interviews.
- Difficulty: Hard
- Topics: Arrays, Dynamic Programming, Sorting, Binary Search
- Asked at: Amazon, Google, Microsoft, Airbnb, PhonePe, Swiggy
- 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
There are n jobs. Job i runs from startTime[i] to endTime[i] and pays profit[i].
Choose a set of jobs in which no two overlap in time, so that the total profit is as large as possible, and return that total. A job that ends at time X does not overlap a job that starts at time X — you may go straight from one to the other.
Example 1
Input: startTime = [1,2,4,6], endTime = [3,5,6,9], profit = [20,30,25,40]
Output: 85
Explanation: Jobs 0, 2 and 3 run back to back (1–3, 4–6, 6–9) for 20 + 25 + 40.
Example 2
Input: startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4]
Output: 6
Explanation: All three overlap, so only one can be taken.
Example 3
Input: startTime = [1,3,2,5,7], endTime = [4,6,8,9,10], profit = [30,40,90,25,20]
Output: 90
Constraints
1 <= startTime.length == endTime.length == profit.length <= 5 * 10^41 <= startTime[i] < endTime[i] <= 10^91 <= profit[i] <= 10^4
Hints for Maximum Profit in Job Scheduling
Hint 1
Sort the jobs by end time. Then the best schedule among the first
ijobs either skips jobior ends with it.Hint 2
If it ends with job
i, every other chosen job must end bystartTimeof jobi— and those jobs form a prefix of the sorted order.Hint 3
Let
dp[i]be the best profit using the firstijobs in end order. Binary-search the numberjof jobs whose end is<= startof jobi, and setdp[i] = max(dp[i-1], dp[j] + profit).
How to solve Maximum Profit in Job Scheduling
Weighted interval scheduling. Once jobs are ordered by end time, the jobs compatible with job i that come before it are exactly a prefix of that order — the ones ending no later than job i starts — so a binary search finds where to continue the DP.
Approach
- Sort job indices by end time; keep the sorted end times in an array
ends. dp[0] = 0. For thet-th job in this order (1-based) with startsand profitp:- Find
j= the number of jobs whose end is<= s(upper bound ofsinends). dp[t] = max(dp[t-1], dp[j] + p)— skip the job, or take it after the best schedule of the firstjjobs.- Return
dp[n].
Why it works
Consider an optimal schedule of the first t jobs. If it does not use job t, its value is dp[t-1]. If it does, the other jobs end by s, and any job ending by s ends strictly before job t does, so they all lie among the first j jobs; conversely every one of those first j jobs is compatible with job t. The best way to use them is dp[j], so the recurrence covers both cases exactly.
Complexity
- Time —
O(n log n) - Space —
O(n)
Pitfalls
- Touching endpoints are allowed: search for ends
<= start, not< start. - Sort by end time; sorting by start breaks the prefix property the binary search relies on.
- Greedy choices (most profitable first, earliest end first) both fail on weighted jobs.
Reference solution
Python
from typing import List
from bisect import bisect_right
def jobScheduling(startTime: List[int], endTime: List[int], profit: List[int]) -> int:
n = len(startTime)
order = sorted(range(n), key=lambda i: endTime[i])
ends = [endTime[i] for i in order]
dp = [0] * (n + 1)
for t in range(1, n + 1):
i = order[t - 1]
j = bisect_right(ends, startTime[i])
dp[t] = max(dp[t - 1], dp[j] + profit[i])
return dp[n]Java
public static int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
int n = startTime.length;
Integer[] order = new Integer[n];
for (int i = 0; i < n; i++) order[i] = i;
Arrays.sort(order, (a, b) -> Integer.compare(endTime[a], endTime[b]));
int[] ends = new int[n];
for (int i = 0; i < n; i++) ends[i] = endTime[order[i]];
int[] dp = new int[n + 1];
for (int t = 1; t <= n; t++) {
int i = order[t - 1], s = startTime[i];
int lo = 0, hi = n;
while (lo < hi) {
int mid = (lo + hi) >>> 1;
if (ends[mid] <= s) lo = mid + 1; else hi = mid;
}
dp[t] = Math.max(dp[t - 1], dp[lo] + profit[i]);
}
return dp[n];
}C++
int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
int n = startTime.size();
vector<int> order(n);
for (int i = 0; i < n; i++) order[i] = i;
sort(order.begin(), order.end(), [&](int a, int b) { return endTime[a] < endTime[b]; });
vector<int> ends(n);
for (int i = 0; i < n; i++) ends[i] = endTime[order[i]];
vector<int> dp(n + 1, 0);
for (int t = 1; t <= n; t++) {
int i = order[t - 1];
int j = upper_bound(ends.begin(), ends.end(), startTime[i]) - ends.begin();
dp[t] = max(dp[t - 1], dp[j] + profit[i]);
}
return dp[n];
}JavaScript
var jobScheduling = function(startTime, endTime, profit) {
var n = startTime.length;
var order = [];
for (var i = 0; i < n; i++) order.push(i);
order.sort(function(a, b) { return endTime[a] - endTime[b]; });
var ends = order.map(function(i) { return endTime[i]; });
var dp = new Array(n + 1).fill(0);
for (var t = 1; t <= n; t++) {
var i = order[t - 1], s = startTime[i];
var lo = 0, hi = n;
while (lo < hi) {
var mid = (lo + hi) >> 1;
if (ends[mid] <= s) lo = mid + 1; else hi = mid;
}
dp[t] = Math.max(dp[t - 1], dp[lo] + profit[i]);
}
return dp[n];
};Also on the editorial tab: C, C#, Go, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 1199 arrays problems · the whole catalogue
Learn the technique: Arrays · Dynamic Programming