Task Scheduling
It's Sunday night and you have a pile of homework. Each assignment earns you points if you turn it in by its deadline, and each takes exactly one hour. You can only work on one at a time. Which ones do you do?
If you're smart about it: grab the most valuable homework first. Schedule it in the latest available slot before its deadline (so you leave earlier slots open for other tasks). Skip anything that no longer has a free slot.
That's task scheduling with deadlines in one paragraph. The trick is making that "find the latest free slot" step fast.
Formalising the Problem
You have n unit-time tasks (each takes exactly 1 time slot). Task i has:
• deadline d_i — must be completed in time slot 1..d_i
• profit p_i — earned only if completed by its deadline
You can execute at most one task per slot. Goal: choose which tasks to run to maximize total profit.
The Greedy Strategy
1. Sort tasks by profit descending.
2. For each task (in profit order), find the latest free slot at or before its deadline.
3. If a free slot exists, assign the task to that slot and mark the slot used.
4. If no free slot, skip the task.
Assigning to the latest available slot (rather than the earliest) is deliberate — it keeps earlier slots open for future high-profit tasks that might have tighter deadlines.
Fast Slot Lookup with Union-Find
The naive approach: for each task, scan backwards from its deadline until you find a free slot. That's \(O(n)\) per task, \(O(n^2)\) total — too slow.
The smart approach: maintain a Union-Find (disjoint set) structure where parent[t] points to the latest free slot ≤ t. When slot t is used, union(t, t−1) so future queries for ≤ t automatically skip to the next available slot. Each query takes near-\(O(1)\) amortized time (inverse Ackermann function). Total: \(O(n \log n)\) for the sort, \(O(nα(n)\)) for the scheduling — sort dominates.
Why Greedy Is Correct — Matroid Theory
The collection of all feasible task sets (sets that can all be completed on time) forms a matroid. For any weighted matroid, the greedy algorithm — sort by weight descending, add an element if the set remains independent — finds the maximum-weight independent set. This is the deep theoretical reason greedy works here: feasibility has exactly the structure of matroid independence.
Key Feasibility Check
A set of tasks S is feasible if and only if for every time t, the number of tasks in S with deadline ≤ t is at most t. Intuitively: you can't do more tasks than time slots exist up to any deadline. This condition can be checked efficiently with Union-Find.