The O(n) Club: Maximum Profit in Job Scheduling: How Not to Overbook Your Calendar (or Lose Your Mind)

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, where dp[i] means max profit up to job i.
  • For each job i (in order):
    • Use binary search to find the last job j where jobs[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.

💻 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 by end.
  • 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.”

Previous Article

The O(n) Club: Russian Doll Envelopes—How to Stuff Smarter, Not Harder

Next Article

The O(n) Club: Pairs of Songs With Total Durations Divisible by 60 — Now That’s What I Call Modulo!