The problem requires finding the maximum profit from scheduling non-overlapping
jobs.
Key observations are that we need to
sort jobs by end time
to
efficiently find non-overlapping ones and use
dynamic programming
to
store intermediate results.
For each job, we decide whether to
include it
(plus profit
from last non-overlapping job) or
exclude it
(carry forward
previous profit). Here,
binary search
helps
quickly find the latest non-overlapping job.
class Job{ int startTime; int endTime; int profit; Job(int start,int end, int profit){ this.startTime = start; this.endTime = end; this.profit = profit; } } class Solution { // Will use binary search to find the non overlapping job // Returns the latest job that ends before the current job starts. public int srch(List<Job> allJobs, int currentJob){ int start = 0; int end = currentJob-1; // As it is 0-based indexing i.e why end=idx-1 int ans = -1; while(start<=end){ int mid = (start+end)/2; // Compare endTime of mid with the startTime of element present idx position if(allJobs.get(mid).endTime <= allJobs.get(currentJob).startTime){ start = mid+1; ans = mid; } else end = mid-1; } return ans; } // Using Bottom Up DP public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { int noOfJobs = startTime.length; List<Job> allJobs = new ArrayList<>(); for(int i=0;i<noOfJobs;i++){ allJobs.add(new Job(startTime[i],endTime[i],profit[i])); } // Sorting by endTime ensures that for any Job i, all possible non-overlapping // jobs (ending before ith job) appear before it in sorted order. Collections.sort(allJobs, (a,b) -> Integer.compare(a.endTime,b.endTime)); int[] maxProfit = new int[noOfJobs+1]; maxProfit[0] = 0; // If there are no jobs, then maxProfit = 0 for(int i=1;i<=noOfJobs;i++){ int include = allJobs.get(i-1).profit; int prevNonOverlappingIndex = srch(allJobs,i-1); // As prevNonOverlappingJob idx will come as zero based. Therefore, dp state // corresponding to it will be represented by t[prevNonOverlappingJob+1]; if(prevNonOverlappingIndex!=-1) include += maxProfit[prevNonOverlappingIndex+1]; int exclude = maxProfit[i-1]; maxProfit[i] = Math.max(include,exclude); } return maxProfit[noOfJobs]; } } /* Golden Note: 1. Sort by endTime when we need to efficiently find the latest non-overlapping event. 2. Sort by startTime when we merge intervals or checking overlaps. */
Sorting takes O(n log n) time and each binary search operation takes O(log n) time for n jobs.
We use O(n) space to store the jobs and the DP array.