When you pick a number x, you earn all points from every occurrence of x, but you must delete all instances of x - 1 and x + 1. This creates a choice at each distinct value: either take it (and skip the next consecutive value) or skip it. Sorting helps group identical values together, making it easy to sum all occurrences. We recursively explore both choices at each group of identical numbers.
dfs(i) starting at index i:i is out of bounds, return 0.nums[i] and move i past this group.dfs(new_i).nums[i] + 1 (they would be deleted if we pick).pick + dfs(after_skipping).dfs(0) and return the result.class Solution:
def deleteAndEarn(self, nums: List[int]) -> int:
nums.sort()
def dfs(i):
if i >= len(nums):
return 0
cur = nums[i]
pick = 0
while i < len(nums) and nums[i] == cur:
pick += nums[i]
i += 1
res = dfs(i)
while i < len(nums) and nums[i] == 1 + cur:
i += 1
res = max(res, pick + dfs(i))
return res
return dfs(0)The recursive solution has overlapping subproblems since we may revisit the same index multiple times. By precomputing the total value for each unique number (sum of all occurrences) and using memoization, we avoid redundant calculations. The problem reduces to: for each unique number, decide whether to take it (earning its total value but skipping the next consecutive number) or skip it.
-1.dfs(i):i is out of bounds, return 0.memo[i] is set, return it.nums[i]: its total value plus dfs(i + 2) if the next number is consecutive, else dfs(i + 1).max(take, dfs(i + 1)).dfs(0).class Solution:
def deleteAndEarn(self, nums: List[int]) -> int:
val = defaultdict(int)
for num in nums:
val[num] += num
nums = sorted(list(set(nums)))
memo = [-1] * len(nums)
def dfs(i):
if i >= len(nums):
return 0
if memo[i] != -1:
return memo[i]
res = val[nums[i]]
if i + 1 < len(nums) and nums[i] + 1 == nums[i + 1]:
res += dfs(i + 2)
else:
res += dfs(i + 1)
res = max(res, dfs(i + 1))
memo[i] = res
return res
return dfs(0)We can convert the top-down solution to bottom-up by processing unique numbers from right to left. At each position, we compute the maximum points achievable from that position onward. If the current and next numbers are consecutive, taking the current means skipping the next; otherwise, we can take the current and continue from the next.
n + 1 initialized to 0.take as the value of the current number plus dp[i + 2] if the next is consecutive, else dp[i + 1].dp[i] = max(dp[i + 1], take).dp[0].class Solution:
def deleteAndEarn(self, nums: List[int]) -> int:
val = defaultdict(int)
for num in nums:
val[num] += num
nums = sorted(list(set(nums)))
dp = [0] * (len(nums) + 1)
for i in range(len(nums) - 1, -1, -1):
take = val[nums[i]]
if i + 1 < len(nums) and nums[i + 1] == nums[i] + 1:
take += dp[i + 2]
else:
take += dp[i + 1]
dp[i] = max(dp[i + 1], take)
return dp[0]Instead of working with unique sorted numbers, we can use an array indexed by the numbers themselves (0 to max). Each index stores the total points for that number. This transforms the problem into the classic House Robber problem: you cannot take adjacent indices. We iterate backward, and at each position, choose the maximum of skipping it or taking it plus the result two positions ahead.
m in the array.m + 2 initialized to 0.dp[num] (accumulating total points per number).m - 1 down to 1:dp[i] = max(dp[i + 1], dp[i + 2] + dp[i]).dp[1].Where is the maximum element in the array and is the size of the array.
We only need to track the maximum earnings for the previous two positions, similar to the space-optimized House Robber solution. By iterating through sorted unique numbers and maintaining two variables, we can reduce space complexity. When consecutive numbers differ by more than 1, there is no conflict, so we can add the current earnings directly.
earn1 = 0 and earn2 = 0 to track the max earnings at the previous two positions.earn2 = max(curEarn + earn1, earn2).earn2 = curEarn + earn2.earn1 to the old earn2 before modifying.earn2.class Solution:
def deleteAndEarn(self, nums: List[int]) -> int:
count = Counter(nums)
nums = sorted(list(set(nums)))
earn1, earn2 = 0, 0
for i in range(len(nums)):
curEarn = nums[i] * count[nums[i]]
if i > 0 and nums[i] == nums[i - 1] + 1:
temp = earn2
earn2 = max(curEarn + earn1, earn2)
earn1 = temp
else:
temp = earn2
earn2 = curEarn + earn2
earn1 = temp
return earn2