Skip to content

1235. Maximum Profit in Job Scheduling

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, you need to output the maximum profit you can take such that there are no 2 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: IMAGE

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:

IMAGE

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:

IMAGE

Input: startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4] Output: 6

Constraints:

1 <= startTime.length == endTime.length == profit.length <= 5 * 10^4 1 <= startTime[i] < endTime[i] <= 10^9 1 <= profit[i] <= 10^4

Analysis

For this problem, we cannot use a greedy algorithm, since each interval's contribution isn't uniform (not all 1), so we need DP to solve it. Different from a classic DP problem, where we need to store all the timestamps, in this problem, we just care about the timestamps that are one of the ending times of any task. For example:

// if t is between two intervals (w/o any intersection), dp[t] is equal to [dp[job1.end], dp[job1.start]], we don't need to record all the timestamps
job1: -----[ t ]
job2:           ------

Now we can define our dp[i] as the maximum profit from the (0, i] timestamp range. We can derive that for any dp[i]: * if we choose to take job x, dp[x.end] = dp[last job end before x.start] + job x's profit * if we choose not to take job x, dp[x.end] = dp[last job's end]

Our answer will be dp[last job ending time].

Time: O(n \log(n)) for sorting, O(n \log(n)) for searching the last job ending (choose case from dp) Space: O(n) we need to store all the intervals in the map.

class Solution {
public:
    int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
        int n = startTime.size();
        vector<vector<int>> jobs;
        for (int i = 0; i < n; ++i) {
            jobs.push_back({endTime[i], startTime[i], profit[i]});
        }
        sort(jobs.begin(), jobs.end()); // sort by ending time
        map<int, int> dp = {{0, 0}};
        for (auto job : jobs) {
            // find the last ending which ends before the current start time, so that we can schedule the current job
            int last_ending_profit = prev(dp.upper_bound(job[1])) -> second; 
            // if adding the current job will cause a higher profit, we can update the current ending time's profit
            if (last_ending_profit + job[2] > dp.rbegin() -> second)
                dp[job[0]] = last_ending_profit + job[2];
        }
        return dp.rbegin() -> second;
    }
};

Misc

In C++, dp.rbegin() will return the last sorted pair from the map, whereas dp.end() should just return an empty iterator. Source: http://www.cplusplus.com/reference/map/map/end/