Given certain jobs with start and end time and amount you make on finishing the job, find the maximum value you can make by scheduling jobs in non-overlapping way.

 

Dynamic programming solution. O(N^2) runtime, O(N) space

First sort all the jobs in ascending order based on their end time. This makes checking if two jobs overlap or not easier. 

State: maxGain[i]: Given the first i jobs, the max profit we can get by selecting jobs[i]. 

Function: maxGain[i] = max {maxGain[i], maxGain[j] + jobs[i].gain}, for j in [0, i)

Initialization: maxGain[i] = jobs[i].gain, as we know by selecting jobs[i], we at least have a profit of jobs[i].gain.

Answer:  the max value of maxGain[i]. 

 

 1 import java.util.Arrays;
 2 
 3 class Job implements Comparable<Job>{
 4     int start;
 5     int end;
 6     int gain;
 7     Job(int start, int end, int gain) {
 8         this.start = start;
 9         this.end = end;
10         this.gain = gain;
11     }
12     public int compareTo(Job o) {
13         return this.end - o.end;
14     }
15 }
16 public class WeightedJobScheduling {
17     public int getMaxGain(Job[] jobs) {
18         Arrays.sort(jobs);
19         int[] maxGain = new int[jobs.length];
20         for(int i = 0; i < maxGain.length; i++) {
21             maxGain[i] = jobs[i].gain;
22         }
23         for(int i = 1; i < maxGain.length; i++) {
24             for(int j = 0; j < i; j++) {
25                 if(jobs[i].start >= jobs[j].end) {
26                     maxGain[i] = Math.max(maxGain[i], maxGain[j] + jobs[i].gain);
27                 }
28             }
29         }
30         
31         int max = Integer.MIN_VALUE;
32         for(int i = 0; i < maxGain.length; i++) {
33             if(maxGain[i] > max) {
34                 max = maxGain[i];
35             }
36         }
37         return max;
38     }
39 }

 

This is the same problem with https://leetcode.com/problems/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:

[Coding Made Simple / LeetCode 1235] Maximum Profit in Job Scheduling

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:

[Coding Made Simple / LeetCode 1235] Maximum Profit in Job Scheduling

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:

[Coding Made Simple / LeetCode 1235] Maximum Profit in Job Scheduling

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

Given the input size of 5 * 10^4, the previous O(N^2) solution is too slow. The bottleneck is that the O(N^2) solution requires O(N) time to find the maximum job profit from 0 to i - 1 jobs that does not overlap with the ith job. Since we already sorted all the jobs based on their end time, we know that for two index j and k, if j < k, if the kth job does not overlap the ith job, the jth job does not overlap with the ith job either. If we can make sure that the biggest index of j < i gives us the max profit from scheduling 0 to jth jobs, we can apply binary search to speed up the runtime from O(N) to O(logN). 

 

We achieve the above goal by redefining dp[i] to be the max profit out of 0 to the ith jobs,  not the max profit out of 0 to the ith jobs with the ith job scheduled.  The dynamic programming state definition in the O(N^2) solution forces us to check each smaller index in order to find the max profit that does not overlap the ith job. 

 

state: dp[i]: max profit out of 0 to ith jobs
dp[i] = Math.max(dp[i - 1], max profit out of 0 to ith jobs, including the ith job)

 

O(N * log N) runtime

class Solution {
    class Job {
        int start, end, profit;
        Job(int start, int end, int profit) {
            this.start = start;
            this.end = end;
            this.profit = profit;
        }
        int getEnd() {
            return end;
        }
    }
    public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
        int n = profit.length;
        Job[] jobs = new Job[n];
        for(int i = 0; i < n; i++) {
            jobs[i] = new Job(startTime[i], endTime[i], profit[i]);
        }
        Arrays.sort(jobs, Comparator.comparingInt(Job::getEnd));
        
        //dp[i]: max profit out of 0 to ith jobs
        //dp[i] = Math.max(dp[i - 1], max profit out of 0 to ith jobs, including the ith job)
        int[] dp = new int[n];
        dp[0] = jobs[0].profit;
        
        for(int i = 1; i < n; i++) {
            int currProfit = jobs[i].profit;
            //find the max profit out of all 0 to i - 1 jobs that has no intersection with the current job.
            //A linear search causes TLE; since we already sorted jobs based on their end time and the dp
            //array is sorted in non-decreasing nature(always pick the max possible value). This means as long
            //as we find the largest j, j < i, that jobs[j].endTime <= jobs[i].startTime, we can just skip all
            //the rest jobs k with k < j, because dp[k] <= dp[j].
            int lastNonOverLapIdx = binarySearch(jobs, 0, i - 1, jobs[i].start);
            currProfit += lastNonOverLapIdx >= 0 ? dp[lastNonOverLapIdx] : 0;
            dp[i] = Math.max(currProfit, dp[i - 1]);
        }
        return dp[n - 1];
    }
    private int binarySearch(Job[] jobs, int l, int r, int t) {
        while(l < r - 1) {
            int mid = l + (r - l) / 2;
            if(jobs[mid].end <= t) {
                l = mid;
            }
            else {
                r = mid - 1;
            }
        }
        if(jobs[r].end <= t) {
            return r;
        }
        else if(jobs[l].end <= t) {
            return l;
        }
        return -1;
    }
}

 

相关文章:

  • 2021-10-09
  • 2021-11-09
  • 2021-06-04
  • 2021-10-07
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-06-01
猜你喜欢
  • 2022-12-23
  • 2021-08-02
  • 2021-12-04
  • 2022-12-23
  • 2021-09-26
  • 2022-02-10
相关资源
相似解决方案