Skip to main content

šŸ’Ž Delete and Earn - The Discovery Journey

The Problem (Quick Glance)​

LC 740: Delete and Earn

You are given an integer array nums. You want to maximize the number of points you get by performing the following operation any number of times:

  • Pick any nums[i] and delete it to earn nums[i] points. Afterwards, you must delete every element equal to nums[i] - 1 and every element equal to nums[i] + 1.

Return the maximum number of points you can earn by applying the above operation some number of times.

Example 1: nums = [3,4,2]

Input: nums = [3,4,2]
Output: 6
Explanation:
- Delete 4 to earn 4 points. Consequently, 3 is also deleted. nums = [2].
- Delete 2 to earn 2 points. nums = [].
You earn a total of 6 points.

Example 2: nums = [2,2,3,3,3,4]

Input: nums = [2,2,3,3,3,4]
Output: 9
Explanation:
- Delete a 3 to earn 3 points. All 2's and 4's are also deleted. nums = [3,3].
- Delete a 3 again to earn 3 points. nums = [3].
- Delete a 3 once more to earn 3 points. nums = [].
You earn a total of 9 points.

Constraints:

  • 1 <= nums.length <= 2 * 10^4
  • 1 <= nums[i] <= 10^4

Initial Observations:

  • This is a maximization problem - maximize points earned
  • Taking number i forces deletion of i-1 and i+1
  • This constraint is similar to House Robber - can't take adjacent houses!
  • We can take ALL occurrences of the same number

Let's start solving!


1. My First Approach: Thinking Recursively​

My thought process:

"When I see a maximization problem with constraints, I think: this is a decision tree problem.

At each number, I can:

  • Take it (earn ALL occurrences, but lose access to adjacent values)
  • Skip it (keep access to adjacent values)

This smells like recursion!"

Solution 1: Unoptimized Recursive (Brute Force)​

class Solution {
public int deleteAndEarn(int[] nums) {
return func(0, 0, nums);
}

int func(int index, int totalVal, int[] nums) {
// Base case
// If we don't have any number left to process, then return totalVal
if (nums.length == 0) {
return totalVal;
}
if (index >= nums.length) {
return totalVal;
}

// Take it - earn ALL occurrences of this number
int currentElement = nums[index];
int earnedPoints = 0;
for (int num : nums) {
if (num == currentElement) {
earnedPoints += num;
}
}
int a = func(0, totalVal + earnedPoints, removeElement(nums, currentElement));

// Leave it
int b = func(index + 1, totalVal, nums);

return Math.max(a, b);
}

// A method for removing all the numbers that are equal to element+1 and element-1
public static int[] removeElement(int[] arr, int element) {
// Count how many elements to keep
int count = 0;
for (int num : arr) {
if (num != element + 1 && num != element - 1 && num != element) {
count++;
}
}

// Create a new array with the correct size
int[] newArr = new int[count];
int idx = 0;
for (int num : arr) {
if (num != element + 1 && num != element - 1 && num != element) {
newArr[idx++] = num;
}
}

return newArr;
}
}

Complexity Analysis:

  • Time: Exponential - Creating new arrays and exploring all decisions
  • Space: O(n²) - Multiple array copies created

Why This Is Slow:

The Problems
  1. Creating new arrays on every recursive call - expensive memory operations
  2. Recalculating the same subproblems multiple times
  3. No memoization - can't cache results because we're modifying the array

For nums = [2,2,3,3,3,4]:

  • Pure recursion explores all possible decision trees
  • Creates multiple copies of modified arrays
  • This is extremely inefficient!

My realization: "Wait, I'm creating new arrays repeatedly. There must be a better way to model this problem!"


2. Optimization Round 1: Adding Memoization (Top-Down DP)​

COMING SOON

This section will be added soon! We'll transform this into a proper DP solution by recognizing it's actually House Robber in disguise!

The Key Insight: Instead of modifying arrays, we can:

  1. Count total points for each number value
  2. Apply House Robber logic: dp[i] = Math.max(dp[i-1], dp[i-2] + points[i])

Solution 2: Top-Down DP with Memoization​

// Coming soon...

3. Optimization Round 2: Going Iterative (Bottom-Up DP)​

COMING SOON

This section will be added soon! We'll build the solution iteratively from smallest values to largest.

The approach:

  1. Create a points[] array where points[i] = total points for number i
  2. Use dynamic programming: for each value, decide to take it or skip it
  3. Can't take consecutive values (like House Robber)

Solution 3: Bottom-Up DP with Tabulation​

// Coming soon...

4. Final Optimization: Space Optimization (O(1) Space)​

COMING SOON

This section will be added soon! Since we only need the last 2 values, we can optimize to O(1) space.

Solution 4: Space-Optimized Bottom-Up​

// Coming soon...

5. Solution Evolution Summary​

COMING SOON
ApproachTimeSpaceKey Insight
1. Unoptimized RecursiveExponentialO(n²)Establishes the problem structure
2. Top-Down (Memoization)O(n)O(n)Recognize House Robber pattern
3. Bottom-Up (Tabulation)O(n)O(n)Build solution iteratively
4. Space-OptimizedO(n)O(1)Only need last 2 values

6. Deep Dive: Understanding Why This Works​

COMING SOON

We'll explore:

  • Why this is House Robber in disguise
  • How to transform array problems into value-based DP
  • The connection between constraints and state definition

7. Mastery & Key Takeaways​

7.1 Current Understanding​

Pattern Recognition

This problem teaches us:

  1. āœ… Look for hidden patterns - Delete and Earn is actually House Robber!
  2. āœ… Transform the problem - Don't work with indices, work with values
  3. āœ… Count occurrences - Multiple same numbers can be grouped
  4. āœ… Adjacent constraints - Taking i blocks i-1 and i+1 (like adjacent houses)

7.2 The Transformation​

The Key Insight: Instead of thinking about array indices, think about number values!

Original: [3, 4, 2]
↓
Transform to points array:
points[2] = 2 (one occurrence of 2)
points[3] = 3 (one occurrence of 3)
points[4] = 4 (one occurrence of 4)
↓
Now it's House Robber:
Can't take both points[3] and points[4] (adjacent values)
Choose: points[4] + points[2] = 6
More Complete Solutions Coming Soon!

We'll add:

  • Memoized recursive solution
  • Bottom-up DP solution
  • Space-optimized solution
  • Detailed complexity analysis
  • Complete execution traces