The O(n) Club: Maximum Profit in Job Scheduling – How Not to Overbook Your Calendar (or Lose Your Mind)
⚡ TL;DR
You’re given jobs with a start, end, and profit. The goal? Stack your calendar with non-overlapping gigs and maximize profit—without triggering a time-space meltdown. Brute force? Sure, if you like O(n²) and lukewarm coffee. DP plus a spicy binary search is your real BFF in Java-land:
// Brute force: for those who love timeouts for (int i = 0; i < n; ++i) { for (int j = 0; j < i; ++j) { if (jobs[j].end <= jobs[i].start) { dp[i] = Math.max(dp[i], dp[j] + jobs[i].profit); } } }
🧠 How Devs Usually Mess This Up
Greedy feels so right and is so, so wrong. Grabbing the highest profit or earliest end time job makes you feel smart—until you realize you left stacks of money on the table. The optimal solution? It’s hiding in combos, not solos. Skipping binary search in your DP? Enjoy your O(n²) regrets. Extra bonus: If you treat jobs that butt up against each other as overlapping, you’re officially in off-by-one hell. Don’t go there.
🔍 What’s Actually Going On
You’re like a chef with a menu of gigs—some short but quick, others long and lucrative, many overlapping like a plate of spaghetti code. The right approach? Sort jobs by end time, then—at each new one—ask: should I take this and the best non-overlapping chunk before it, or skip it and keep my old profit? Binary search lets you find the best previous job in log time. This is dynamic programming in its “I told you so” form, letting you construct the most profitable path one interval at a time.
🛠️ PseudoCode
- Make a
Job
class with start, end, profit. - Sort all jobs by their
end
time.Arrays.sort(jobs, Comparator.comparingInt(j -> j.end));
- Create a
dp[]
array, wheredp[i]
means max profit up to jobi
. - For each job
i
(in order):- Use binary search to find the last job
j
wherejobs[j].end <= jobs[i].start
. - Option 1: Don’t use current job:
dp[i-1]
. - Option 2: Use current job:
jobs[i].profit + (prev >= 0 ? dp[prev] : 0)
. - Set
dp[i]
to the max of both options.
- Use binary search to find the last job
💻 The Code
class Job {
int start, end, profit;
Job(int s, int e, int p) { start = s; end = e; profit = p; }
}
public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
int n = startTime.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(j -> j.end));
int[] dp = new int[n];
dp[0] = jobs[0].profit;
for (int i = 1; i < n; ++i) {
int prev = binarySearch(jobs, i, jobs[i].start);
dp[i] = Math.max(dp[i-1], jobs[i].profit + (prev >= 0 ? dp[prev] : 0));
}
return dp[n-1];
}
// Binary search: find last job with jobs[j].end <= start
private int binarySearch(Job[] jobs, int right, int start) {
int l = 0, r = right - 1;
while (l <= r) {
int m = l + (r - l) / 2;
if (jobs[m].end <= start) l = m + 1;
else r = m - 1;
}
return r;
}
⚠️ Pitfalls, Traps & Runtime Smacks
- end == start? Not overlapping. Schedule back-to-back, rake in the cash. Lots mess this up and lose profit.
- Binary search off-by-one: If you fumble your pointers,
dp
explodes. Always ensure you’re finding the LAST job that actually ends before (or at) the next one starts. - Sorting by the wrong thing: Sorting by
start
time or even profit? That’s a spicy runtime bug. Always byend
. - Brute force regret: Your interview and laptop fans will both scream in O(2^n). O(n log n) keeps you chill.
🧠 Interviewer Brain-Tattoo
“Greedy gets you pizza, not max profit. Real winners sort, binary search, and let DP do the heavy lifting.”