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]-1和nums[i]+1,也就是nums[i]的值加一减一。
看懂了这个,就可以发现这个问题和数组的顺序是没有关系的。只和有哪些数字,分别有多少个有关。
以nums = [2, 2, 3, 3, 3, 4]为例。
- 找到其中的最大值
nums_max = 4 - 新建一个
nums_sum数组,size为nums_max+1,默认值是0 - 遍历
nums,nums_sum[n] += n,得到的结果[0, 0, 4, 9, 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]的最大得分 - 根据
dp[0][i-1],dp[1][i-1]和dp[1][i-2]的值更新dp 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步的流程显示出来的话是这样的

最上方是index,然后是nums_sum,下面是dp的一步步计算。
- step 0: 选不选0都是0
- step 1: 因为数组里没有1,所以肯定选不了,因此
dp[0][1] = dp[1][0] + 0,dp[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 = 2,nums[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 都不选才能有最大值。
