Transform the problem into House Robber dynamic programming by grouping equal values into total points.
Problem Restatement
We are given an integer array nums.
We may repeatedly choose a number x.
When we choose x:
- We earn
xpoints. - Every occurrence of
x - 1is deleted. - Every occurrence of
x + 1is deleted.
We can continue until no numbers remain.
We need to return the maximum total points we can earn.
The official problem states that after taking a value x, every occurrence of x - 1 and x + 1 becomes unavailable. (leetcode.com)
Input and Output
| Item | Meaning |
|---|---|
| Input | A list of integers nums |
| Output | Maximum obtainable points |
Choosing x | Earns x points per occurrence chosen |
| Side effect | Deletes all x - 1 and x + 1 values |
Example function shape:
def deleteAndEarn(nums: list[int]) -> int:
...Examples
Example 1
nums = [3, 4, 2]If we choose 4, then 3 is deleted.
That gives:
4 pointsIf we choose 2, nothing conflicts with it afterward.
Total:
4 + 2 = 6The answer is:
6Example 2
nums = [2, 2, 3, 3, 3, 4]The total value for each number is:
| Number | Total Points |
|---|---|
| 2 | 4 |
| 3 | 9 |
| 4 | 4 |
If we take 3, then 2 and 4 are removed.
That gives:
9If we instead take 2 and 4, we get:
4 + 4 = 8So the best answer is:
9First Thought: Recursive Choices
At each step, we can choose some value x.
Then values x - 1 and x + 1 become forbidden.
This looks like a recursive decision problem.
But directly simulating deletions is complicated and inefficient.
The important observation is that only the numeric values matter, not their positions in the array.
Key Insight
Group equal numbers together.
Suppose:
nums = [2, 2, 3, 3, 3, 4]Instead of thinking about individual elements, think about total gain per value:
2 -> 4
3 -> 9
4 -> 4Now the problem becomes:
choose values without taking adjacent numbersThis is exactly the same structure as House Robber.
In House Robber:
- Robbing house
iforbids housei - 1andi + 1.
Here:
- Taking value
xforbidsx - 1andx + 1.
So we convert the problem into a linear DP over values.
Algorithm
- Count the total points contributed by each value.
- Let:
points[x]store the total score from value x.
- Process values from
0tomax(nums).
For each value x:
- Either skip it.
- Or take it and add
points[x]to the best answer up tox - 2.
Use House Robber DP:
dp[x] = max(
dp[x - 1],
dp[x - 2] + points[x]
)Return the final DP value.
Correctness
For every value x, all occurrences of x should either be taken together or skipped together.
If we take one occurrence of x, then all occurrences of x - 1 and x + 1 become unusable. Taking additional copies of x has no additional restriction, so taking all copies of x is always optimal once we decide to take value x.
Therefore the problem reduces to choosing values on the number line.
For each value x, there are exactly two possibilities:
Skip
x.- Then the best total remains
dp[x - 1].
- Then the best total remains
Take
x.- Then
x - 1cannot be taken. - The best compatible earlier result is
dp[x - 2]. - The total becomes
dp[x - 2] + points[x].
- Then
The recurrence:
dp[x] = max(
dp[x - 1],
dp[x - 2] + points[x]
)therefore considers all optimal possibilities.
The DP processes values in increasing order, so all required earlier states are already known.
Thus the final DP value equals the maximum obtainable score.
Complexity
Let:
m = max(nums)| Metric | Value | Why |
|---|---|---|
| Time | O(n + m) | Build totals once, then scan all values |
| Space | O(m) | Store totals and DP states |
Here n is the length of nums.
Implementation
class Solution:
def deleteAndEarn(self, nums: list[int]) -> int:
if not nums:
return 0
max_num = max(nums)
points = [0] * (max_num + 1)
for num in nums:
points[num] += num
dp = [0] * (max_num + 1)
dp[0] = points[0]
if max_num >= 1:
dp[1] = max(points[0], points[1])
for x in range(2, max_num + 1):
dp[x] = max(
dp[x - 1],
dp[x - 2] + points[x]
)
return dp[max_num]Code Explanation
We first handle the empty case:
if not nums:
return 0Then we compute the largest value:
max_num = max(nums)The points array stores the total gain for each number:
points[num] += numFor example:
nums = [2, 2, 3]produces:
points[2] = 4
points[3] = 3The DP array stores the best answer up to each value:
dp = [0] * (max_num + 1)The recurrence is:
dp[x] = max(
dp[x - 1],
dp[x - 2] + points[x]
)This means:
- Skip value
x. - Or take value
xand combine it with the best non-adjacent result.
Finally:
return dp[max_num]returns the optimal answer over all values.
Testing
def run_tests():
s = Solution()
assert s.deleteAndEarn([3, 4, 2]) == 6
assert s.deleteAndEarn([2, 2, 3, 3, 3, 4]) == 9
assert s.deleteAndEarn([1]) == 1
assert s.deleteAndEarn([1, 1, 1, 2, 4, 5, 5, 5, 6]) == 18
assert s.deleteAndEarn([8, 10, 4, 9, 1, 3, 5, 9, 4, 10]) == 37
assert s.deleteAndEarn([]) == 0
print("all tests passed")
run_tests()| Test | Why |
|---|---|
[3, 4, 2] | Basic example |
[2, 2, 3, 3, 3, 4] | Taking middle value beats outer values |
[1] | Minimum non-empty input |
| Mixed repeated values | Checks grouped scoring |
| Larger mixed case | More complex DP transitions |
| Empty array | Edge case handling |