Job Sequencing — First-Free-Slot Bug Costs $50k
Assigning jobs to earliest free slot can cause high-profit jobs to miss deadlines, in a $50k penalty.
- Sort jobs by profit descending: highest profit first.
- For each job, find the latest free slot ≤ its deadline.
- Assign job to that slot — keeping earlier slots open for other jobs.
- Naive O(n²) slot search; Union-Find reduces to near O(n log n).
- Proven optimal via exchange argument: any optimal schedule can be transformed to this greedy result without reducing profit.
You have a list of freelance jobs, each paying different amounts but with a deadline (must be done by day X, one job per day). You want to maximise earnings. The greedy insight: always try to schedule the highest-paying job as late as possible before its deadline — this leaves earlier slots free for other jobs.
Job scheduling with deadlines is a classic operations research problem that appears in OS scheduling, manufacturing planning, and project management. The greedy insight — schedule highest-paying jobs as late as possible within their deadlines — is counterintuitive: you might expect to schedule early, but scheduling late preserves earlier slots for potentially higher-value jobs that might arrive.
The Union-Find optimisation that reduces O(n^2) to O(n log n) is an elegant application of the disjoint set data structure in a non-graph context — a useful demonstration that Union-Find is about more than just connected components.
Greedy Algorithm
Sort jobs by profit descending. For each job, find the latest available slot before its deadline and assign it there.
This is the core greedy choice: take the highest-profit job and defer it as late as possible. Why late? Because early slots are scarce — a job with deadline 1 can only go in slot 1. If you take a low-profit job for slot 1, you block every job with deadline 1. By scheduling high-profit jobs late, you keep earlier slots flexible for jobs that have no choice but to go early.
Union-Find Optimisation — O(n log n)
The naive slot-finding is O(n) per job — O(n²) total. Using Union-Find (disjoint set), we can find the latest free slot in near-O(1) amortised time, giving O(n log n) overall.
How it works: treat each time slot as a node in a Union-Find structure. When a slot is used, union it with the slot before it (slot → slot-1). The find operation returns the largest free slot ≤ given deadline. It's essentially a 'next available lower slot' finder, with path compression making almost constant time.
Complexity
Naive: O(n log n) sort + O(n²) slot finding = O(n²) Union-Find: O(n log n) sort + O(n α(n)) slot finding ≈ O(n log n)
For most interview purposes the naive O(n²) implementation is sufficient. But you should understand both — the interviewer may ask how to optimise when n reaches 10⁵.
Proof of Optimality via Exchange Argument
We need to prove that the greedy algorithm (highest profit first, latest slot) produces maximum total profit. The exchange argument works like this:
- Let G be the set of jobs selected by greedy. Let O be an optimal set with maximum total profit.
- Compare G and O from highest profit to lowest. At the first point they differ, greedy chose job j (higher profit) while optimal chose job k (lower profit or none).
- Since j has higher profit and is scheduled at or after k's slot, we can swap k with j in O without reducing profit and without violating deadlines.
- After a finite number of swaps, O becomes identical to G, proving G is optimal.
This argument holds only because the profit-sorted order is monotonic — the greedy never picks a lower-profit job when a higher-profit one could be placed. The 'latest slot' rule ensures we don't block future jobs unnecessarily.
- Slot 1 can only fit jobs with deadline ≥ 1 — every job qualifies.
- Slot 10 can only fit jobs with deadline ≥ 10 — fewer jobs qualify.
- So slot 1 is the scarcest: fill it last, with a job that absolutely must go there.
- By scanning from deadline down to 1, you effectively 'push' each job as far right as possible.
Variations and Real-World Extensions
The classic job sequencing problem assumes each job takes exactly one time unit. In practice, jobs have varying durations and may depend on each other. Here are common variations:
Weighted Interval Scheduling: Jobs have start/end times and durations. Greedy fails; DP required. Job Sequencing with Release Times: Each job has a release time (earliest start). Greedy still works with slight modification — always pick the job with highest profit among available ones. Multi-Machine Scheduling: Multiple workers/CPUs. NP-hard in general; heuristic approaches used. Minimum Lateness Scheduling: Instead of maximising profit, minimise maximum lateness. Solved by EDD (Earliest Due Date) — a different greedy.
Understanding the classic version provides the foundation for these extensions.
Missed Deadline in a Cloud Batch Scheduler Cost $50k in Penalties
- For deadline-based scheduling, always assign jobs to the latest possible slot — not the earliest.
- Greedy is optimal here, but only when the choice rule and slot selection are both correct.
- Unit test edge cases: all jobs same deadline, all profits equal, large profit disparity.
find() compresses properly; missing path compression leads to O(n²) anyway.sorted() or sort() with key, not custom comparison.Key takeaways
Common mistakes to avoid
5 patternsAssigning jobs to earliest available slot instead of latest
Off-by-one in slot array indexing (0 vs 1 indexed)
Using Union-Find but forgetting path compression in find()
find() sets parent[i] = find(parent, parent[i]) recursively, not just returns parent[i].Sorting by deadline instead of profit
Assuming jobs can be split or run part-time
Interview Questions on This Topic
Why do we assign jobs to the latest possible slot rather than earliest?
Frequently Asked Questions
That's Greedy & Backtracking. Mark it forged?
3 min read · try the examples if you haven't