Abstract
Like Event Scheduling, but now every event has a value — instead of maximizing the count of non-overlapping events, maximize their total value.
- Category: Dynamic Programming / Interval Scheduling (weighted variant)
- Input: A list of events, each with a start time, finish time, and value
- Output: The maximum total value achievable from a non-overlapping subset
- Paradigm: Backtracking (naive) → Dynamic Programming (via memoization)
- Typical use cases: resource allocation where jobs differ in priority/payoff, not just presence — plain Event Scheduling’s “maximize count” greedy strategy no longer works once events carry different values
Problem Specification
- Instance: — start time, finish time, and value for each event.
- Solution: A subset of events .
- Constraints: No two events in overlap.
- Objective: .
- Goal: Maximize the sum.
Why the Old Greedy Strategy Fails
Event Scheduling’s Earliest-End-Time greedy strategy is optimal for maximizing count, but says nothing about value — a short, low-value event finishing early can easily block out a much more valuable event that would have overlapped it. Greedy has no way to “look ahead” and weigh that trade-off, which is exactly why this problem needs a different approach.
Candidate Strategies / Approaches
Backtracking ✘ (exponential)
Strategy: sort events by end time. Consider the last event to end, — including it isn’t necessarily good, so try both possibilities:
- Exclude : recurse on .
- Include : recurse on the set of all intervals that do not conflict with — more precisely, where is the last event to end before starts.
Algorithm 22 Weighted Event Scheduling
Input: list of events
Output: list of events that does not overlap each other and maximizes the value
procedure BTWES()
if then
return
if then
return
Out =
Let be the last event to end before starts
In =
return OutIn
Out costs ; In costs .
Runtime (worst case):
No better than exhaustive search.
The Key Insight: How Many Distinct Calls Are There?
We make up to recursive calls — but every recursive call has the form for some . So there are at most genuinely different calls that ever occur: .
Key Idea
Of the up to recursive calls this algorithm makes, only are actually distinct — the exact same subproblems are being solved over and over along different branches. Memoization — storing and reusing each distinct answer, e.g. in a hashmap or array — is the fix. This is the seed of the full Dynamic Programming solution below.
Dynamic Programming ✔
Instead of top-down recursion with a cache, solve the same distinct subproblems bottom-up, smallest first, filling in an array directly.
Dynamic Programming Solution (The 8 Steps)
1. Define Sub-Problems and Corresponding Array
Hint
The sub-problems are often restatements of the original problem.
- Original Problem: find the max value among all valid schedules of .
- Sub-Problem: let be the max value among all valid schedules of .
2. What Are the Base Cases?
True for any input — the empty schedule has value 0.
3. Give Recursion for Sub-Problems (Case Analysis)
Hint
Break up the sub-problem into distinct cases.
- Case 1: is not part of the max-value schedule .
- Case 2: is part of the max-value schedule , where is the last event before event starts.
(with and , so both terms reference strictly smaller sub-problems.)
4. Order the Sub-Problems
Since each sub-problem depends only on sub-problems of strictly smaller index, order them from up to .
5. What Is the Final Output?
6. Put It All Together: Iterative Algorithm
Algorithm 23 Max Subset
procedure MaxSubset()
//Step 2
for do//Step 4
//Step 3 start
while do
In =
Out =
InOut//Step 3 end
return //Step 5
Variables & Data Structures
| Name | Type | Purpose |
|---|---|---|
A | Array, size | A[k] holds the max value achievable using only events |
j | Index | Found via linear scan — the last event that finishes before starts |
In, Out | Values | The two candidate values for A[k] — including or excluding |
Helper Functions / Operations Used
- Find (last non-conflicting event before ) — linear scan in the pseudocode above, worst case per call to the outer loop; see Drawbacks / Constraints for a faster alternative.
Proof of Correctness / Optimality
Claim: is the max value out of all valid schedules of .
- Base case: (Step 2).
- Inductive Hypothesis: is set correctly for all , for some .
- Inductive Step (Step 3): consider .
- Case 1 — is not in the max-value schedule: the best schedule using only is then just the best schedule using , so — correct by the Inductive Hypothesis.
- Case 2 — is in the max-value schedule: every event that conflicts with (i.e. ) must be excluded, so the best schedule is ‘s value plus the best schedule using only the non-conflicting events : — correct by the Inductive Hypothesis, since .
- Since every valid schedule falls into exactly one of these two cases, and , is the true maximum.
Time & Space Complexity Analysis
General Case
| Complexity | Notes | |
|---|---|---|
| Time | Outer loop runs times; each iteration’s while loop scanning for costs worst case, giving | |
| Space | The array A holds one entry per event |
Best / Worst / Average Case
- Best / Worst / Average case: all with this implementation — the linear scan for runs regardless of how the events happen to be arranged.
Drawbacks / Constraints
- The bound isn’t tight to the DP idea itself — it comes from the linear scan used to find inside the loop. If events are pre-sorted by finish time (already assumed here) and you additionally binary-search for against the sorted start times, this drops to total — the same order as sorting the events in the first place.
- Doesn’t handle changing values/weights dynamically — like most DP solutions, this assumes the full input (including all values) is known up front; recomputation is needed if values change after the array is filled.
- Not suitable for: finding the count-maximizing schedule when all values happen to be equal — Event Scheduling’s simpler greedy strategy is a better fit for that special case, since it doesn’t need the full array at all.
