LeetCode - 04 - 740. Delete and Earn

302 阅读4分钟

Date: 20200327

740. Delete and Earn

Given an array nums of integers, you can perform operations on the array. In each operation, you pick any nums[i] and delete it to earn nums[i] points. After, you must delete every element equal to nums[i] - 1 or nums[i] + 1. You start with 0 points. Return the maximum number of points you can earn by applying such operations.

基本信息

Difficulty: Medium

Related Topics: Dynamic Programming

v0.1.0

读了三遍题目,确定了随着nums[i]被删除的不是nums[i-1],nums[i+1],也就是说不是数组里它两边的数。而是nums[i]-1nums[i]+1,也就是nums[i]的值加一减一。

看懂了这个,就可以发现这个问题和数组的顺序是没有关系的。只和有哪些数字,分别有多少个有关。

nums = [2, 2, 3, 3, 3, 4]为例。

  1. 找到其中的最大值nums_max = 4
  2. 新建一个nums_sum数组,sizenums_max+1,默认值是0
  3. 遍历nums,nums_sum[n] += n,得到的结果[0, 0, 4, 9, 4]
  4. 新建vector<vector<int>> dp,长度为2,每个vector的size是nums_max+1, dp[0][i]代表nums_sum[0]nums_sum[i],选择删除nums_sum[i]的最大得分,dp[1][i]代表nums_sum[0]nums_sum[i],选择不删除nums_sum[i]的最大得分
  5. 根据dp[0][i-1], dp[1][i-1]dp[1][i-2]的值更新dp
  6. max(dp[0][nums_max], dp[1][nums_max])的值就是我们要的结果

这里1-3实际上进行了两次遍历,如果需要提高效率,可以根据题目Note中给的条件

Each element nums[i] is an integer in the range [1, 10000].

直接将nums_sum的长度设置为10001,在遍历的同时更新最大值。

把第5步的流程显示出来的话是这样的

img

最上方是index,然后是nums_sum,下面是dp的一步步计算。

  • step 0: 选不选0都是0
  • step 1: 因为数组里没有1,所以肯定选不了,因此dp[0][1] = dp[1][0] + 0dp[1][1] = dp[0][0]
  • step 2: 有2个2,因此如果选择删除2,积分增加4分,不选择就不增加,dp[0][2] = max(dp[1][1], dp[1][0]) + nums_sum[2], dp[1][2] = dp[0][1]
  • step 3: 有3个3,因此如果选择删除3,积分增加9分,不选择就不增加,dp[0][3] = max(dp[1][2], dp[1][1]) + nums_sum[3], dp[1][3] = dp[0][2]
  • step 4: 有1个4,因此如果选择删除4,积分增加4分,不选择就不增加,dp[0][4] = max(dp[1][3], dp[1][2]) + nums_sum[4], dp[1][4] = dp[0][3]

为什么dp[0][i]max(dp[1][i-1], dp[1][i-2])nums_sum[i]相加?因为有可能出现两个连续的数都不选的情况,如果只考虑dp[1][i-1],实际上只是比较所有奇数相加和所有偶数相加(相隔为1)的和而已。那么有没有可能出现三个连续的数都不选呢?不可能,因为加上中间的那个数最后的总积分一定是比三个都不选多的。所以只需要考虑dp[1][i-1], dp[1][i-2]就可以了。

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        if (nums.size() == 0) {
            return 0;
        }
        int max_int = *max_element(nums.begin(), nums.end());
        
        vector<int> number_sum(max_int+1, 0);
        
        vector<vector<int>> dp(2, number_sum);

        for (int i = 0; i < nums.size(); i++) {
            number_sum[nums[i]] += nums[i];
        }
        
        for (int i = 1; i < number_sum.size(); i++) {
            if (number_sum[i] != 0) {
                if (i == 1) {
                    dp[0][i] = dp[1][i-1] + number_sum[i];
                } else {
                    dp[0][i] = max(dp[1][i-1], dp[1][i-2]) + number_sum[i];
                }
                
                dp[1][i] = dp[0][i-1];
            } else {
                int current_max = max(dp[0][i-1], dp[1][i-1]);
                dp[0][i] = current_max;
                dp[1][i] = current_max;
            }
            
        }
        
        int result = max(dp[0][number_sum.size()-1], dp[1][number_sum.size()-1]);
        
        return result;
        
    }
    
};

Submission

Runtime: 4 ms, faster than 98.06% of C++ online submissions for Delete and Earn. Memory Usage: 6.6 MB, less than 100.00% of C++ online submissions for Delete and Earn.

反思

要认真审题! After, you must delete every element equal to nums[i] - 1 or nums[i] + 1不是删除nums[i-1]和nums[i+1]。比如nums = [2, 2, 3, 3, 3, 4],选择了nums[2],那么nums[i] - 1 = 3 - 1 = 2nums[i] + 1 = 3 + 1 = 4。所以这个问题的结果是9不是10。

还有就是要考虑前面数字不选的时候,不仅要考虑前一个,还要考虑再前面一个。如果只考虑前一个,比如test case [10,8,4,2,1,3,4,8,2,9,10,4,8,5,9,1,5,1,6,8,1,1,6,7,8,9,1,7,6,8,4,5,4,1,5,9,8,6,10,6,4,3,8,4,10,8,8,10,6,4,4,4,9,6,9,10,7,1,5,3,4,4,8,1,1,2,1,4,1,1,4,9,4,7,1,5,1,10,3,5,10,3,10,2,1,10,4,1,1,4,1,2,10,9,7,10,1,2,7,5] 就会得不到正确的答案。

如下图所示,2 和 3 都不选才能有最大值。

img2