How do you solve the house robber problem, where you cannot rob two adjacent houses?
At each house you have two options: rob it and skip the previous one, or skip it and keep whatever you had. The best total up to house i is the larger of the best total up to house i minus one and the best total up to house i minus two plus the value of house i.
def rob(nums):
previous, current = 0, 0
for value in nums:
previous, current = current, max(current, previous + value)
return current
print(rob([2, 7, 9, 3, 1]))
print(rob([5, 1, 1, 5]))The outputs are 12 and 10. In the first array the best plan is to rob houses of value 2, 9 and 1, which are not adjacent, giving twelve. In the second the best is the first and the last house, giving ten.
The mechanism is the two options covering every case. Either you rob the current house, in which case the previous one must be skipped, or you do not rob it, in which case the previous best stands. Since the two cases are exhaustive and disjoint, taking the larger is correct.
The trade-off to name is that the two variable version uses constant space, while the obvious table version uses space linear in the number of houses. As with the climbing stairs problem, the recurrence only needs the last two values, so the whole history is unnecessary unless you want to reconstruct which houses were robbed. The other point is that this is a good example of a problem where a greedy choice fails, because always robbing the richest house can block two good neighbours, and the dynamic program explores both options properly.