1235. Maximum Profit in Job Scheduling
09/19/2021
We have n
jobs, where every job is scheduled to be done from startTime[i]
to endTime[i]
, obtaining a profit of profit[i]
.
You're given the startTime
, endTime
and profit
arrays, return the maximum profit you can take such that there are no two jobs in the subset with overlapping time range.
If you choose a job that ends at time X
you will be able to start another job that starts at time X
.
Example 1:

Input: startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70]
Output: 120
Explanation: The subset chosen is the first and fourth job.
Time range [1-3]+[3-6] , we get profit of 120 = 50 + 70.
Example 2:

Input: startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60]
Output: 150
Explanation: The subset chosen is the first, fourth and fifth job.
Profit obtained 150 = 20 + 70 + 60.
Example 3:

Input: startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4]
Output: 6
Constraints:
1 <= startTime.length == endTime.length == profit.length <= 5 * 104
1 <= startTime[i] < endTime[i] <= 109
1 <= profit[i] <= 104
Basic Idea
我们注意到,对于每个task我们可以选择schedule或者跳过,如果我们选择schedule,则只能在它的endtime之后考虑之后start的其他task,这里是可能会有重复计算的。事实上我们可以在对tasks的start time排序之后,利用cache来存放每个start时间对应的之后所有tasks能获得的最大profit,这样可以把时间复杂度降低到 O(NlogN), 因为一开始的排序和每次的二分。
DFS with Memoization: 利用递归的思路从第一个任务开始,每次尝试schedule或者skip,利用二分法来找到某个时间点之后start的第一个task
DP using TreeMap: 利用TreeMap作为DP,从最后一个开始的task开始,每次检查当前task的profit以及它结束之后开的的tasks中能获得的最大profit之和,以及如果skip当前task所能获得的最大profit
java Code:
// DFS with memoization
class Solution {
public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
Map<Integer, Integer> cache = new HashMap<>();
int[][] tasks = new int[startTime.length][3];
for (int i = 0; i < startTime.length; ++i) {
tasks[i] = new int[] {startTime[i], endTime[i], profit[i]};
}
Arrays.sort(tasks, (t1, t2) -> {
if (t1[0] != t2[0]) return Integer.compare(t1[0], t2[0]);
else return Integer.compare(t2[1], t1[1]); // 结束晚的在前
});
return getMaxProfitStartFrom(tasks[0][0], tasks, cache);
}
private int getMaxProfitStartFrom(int start, int[][] tasks, Map<Integer, Integer> cache) {
int nextIndex = searchNextStartTaskIndex(start, tasks);
if (nextIndex == -1) {
return 0;
}
int[] task = tasks[nextIndex];
if (cache.containsKey(task[0])) {
return cache.get(task[0]);
}
// 分别考虑schedule相同start time的所有task
int maxProfit = 0;
for (int i = nextIndex; i < tasks.length; ++i) {
if (i > nextIndex && tasks[i][0] != task[0]) {
break;
}
maxProfit = Math.max(maxProfit,
tasks[i][2] + getMaxProfitStartFrom(tasks[i][1], tasks, cache));
}
// 考虑 skip 当前start time的所有task
maxProfit = Math.max(maxProfit, getMaxProfitStartFrom(task[0] + 1, tasks, cache));
cache.put(task[0], maxProfit);
return maxProfit;
}
// 找到开始时间>= start的第一个task的index, return -1 if not found
private int searchNextStartTaskIndex(int start, int[][] tasks) {
int left = 0, right = tasks.length - 1;
while (left + 1 < right) {
int mid = (left + right) / 2;
if (tasks[mid][0] < start) {
left = mid;
} else {
right = mid;
}
}
if (tasks[left][0] >= start) return left;
else if (tasks[right][0] >= start) return right;
else return -1;
}
}
//DP:
class Solution {
public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
int N = startTime.length;
TreeMap<Integer, Integer> dp = new TreeMap<>();
int[][] tasks = new int[N][3];
for (int i = 0; i < N; ++i) {
tasks[i] = new int[] {startTime[i], endTime[i], profit[i]};
}
Arrays.sort(tasks, (t1, t2) -> Integer.compare(t1[0], t2[0]));
for (int i = tasks.length - 1; i >= 0; --i) {
int[] task = tasks[i];
int start = task[0], end = task[1], p = task[2];
Map.Entry<Integer, Integer> entryAfterEnd = dp.ceilingEntry(end);
int val = entryAfterEnd == null ? p : p + entryAfterEnd.getValue();
Map.Entry<Integer, Integer> entryAfterStart = dp.firstEntry();
if (entryAfterStart != null && entryAfterStart.getValue() > val) {
dp.put(start, entryAfterStart.getValue());
} else {
dp.put(start, val);
}
}
return dp.firstEntry().getValue();
}
}
Last updated