One changed objective takes us from greedy to DP
Picture
The calendar is unchanged, but appointments now have different rewards. Earliest finish can sacrifice a valuable appointment for cheap ones. Compare taking or skipping each appointment, and jump directly to the next compatible opportunity when you take it.
Predict
Appointments (start,end,reward) are (0,2,2), (2,4,2), and (0,4,10). Which result exposes the limit of the earliest-finish rule?
Reveal the reasoning
Earliest finish collects 4 from two short appointments. Taking the long appointment collects 10. The previous proof preserved the number of appointments, which is no longer the requested objective.
Go deeper: remove repeated compatibility scanning
Sort appointments by start time. best(i) is the maximum reward among appointments from index i onward. Skipping leads to i+1; taking earns its reward and jumps to the first start time at least its end.
from bisect import bisect_left
from functools import cache
def max_reward(appointments):
jobs = sorted(appointments)
starts = [job[0] for job in jobs]
following = [bisect_left(starts, end, lo=i + 1)
for i, (_, end, _) in enumerate(jobs)]
@cache
def best(i):
if i == len(jobs):
return 0
return max(best(i + 1), jobs[i][2] + best(following[i]))
return best(0)
assert max_reward([(0, 2, 2), (2, 4, 2), (0, 4, 10)]) == 10
assert max_reward([(0, 2, 7), (2, 4, 8)]) == 15
assert max_reward([]) == 0
Assume positive duration and half-open intervals; choosing none is permitted. Because starts are sorted, compatibility has a false-then-true boundary, making binary search valid. The recursive dependencies always go to larger indices.
Sorting and successor lookup take O(n log n); the DP evaluates O(n) states with two choices each. Storage is O(n), including cache and stack. A reverse-index table avoids deep recursion.
The state needs only one coordinate: after taking an interval, the jump removes incompatible early starts. You could instead write a state involving current time, but that is not required by this formulation.
Analogy limit: the word “interval” in the story does not mean the algorithm is interval DP. Here the state is a suffix index; interval DP will describe a subproblem bounded by two positions.
Change one rule
You must choose at least one appointment, and all rewards are negative. Is the current zero base and skip behavior still correct?
Check the variation
No. It permits the empty selection with value zero. Track whether a required selection has occurred, or otherwise enforce a nonempty result with suitable feasible and impossible base cases.
Connect
Knapsack also compares take and skip under constraints. Interval DP uses a different state despite a similar name. Recognition separates story words from structural evidence.