740. 删除并获得点数
给你一个整数数组 nums
,你可以对它进行一些操作。
每次操作中,选择任意一个 nums[i]
,删除它并获得 nums[i]
的点数。之后,你必须删除每个等于 nums[i] - 1
或 nums[i] + 1
的元素。
开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。
示例 1:
输入:nums = [3,4,2]
输出:6
解释:
删除 4 获得 4 个点数,因此 3 也被删除。
之后,删除 2 获得 2 个点数。总共获得 6 个点数。
示例 2:
输入:nums = [2,2,3,3,3,4]
输出:9
解释:
删除 3 获得 3 个点数,接着要删除两个 2 和 4 。
之后,再次删除 3 获得 3 个点数,再次删除 3 获得 3 个点数。
总共获得 9 个点数。
提示:
- 1 <= nums.length <= 2 * 104
- 1 <= nums[i] <= 104
方法一:动态规划
思路
打家劫舍 换了一个马甲~
先假设数据量足够,即 [1,10000]
每个数字都有。根据题意 一旦选择了某个数字 x
,x - 1
和 x + 1
都无法选择了,若存在多个 x
,是可以进行累加的。所以每个数字对应的点数是 数字 * 个数
。
对于 [1,10000]
中的每个数字,我们有 选择 或 不选择 两种方案,且不能选择相邻的数字。这就和 打家劫舍 问题一毛一样了~。
定义状态 dp[i]
表示遍历到数字 i
时能获得的最多点数。状态转移方程如下,其中 sum[i] 为 数字 i * i 的个数
:
- 选择数字
i
的累计点数 =dp[i - 2] + sum[i]
- 不选择数字
i
的累计点数 =dp[i - 1]
- 即
dp[i]
= max(dp[i - 2] + sum[i]
,dp[i - 1]
)
细节
- 没有必要构建
[0, 1000]
这么大的数组,根据数组最大值 max 去构建 [0, max] 的数组即可。
参考代码
public int deleteAndEarn(int[] nums) {
// 构建 sum 数组,即 数字 * 个数,作为 [打家劫舍] 问题的入参
int max = Arrays.stream(nums).max().getAsInt();
int[] sum = new int[max + 1];
for (int x : nums) {
sum[x] += x;
}
return rob(sum);
}
// 打家劫舍代码
public int rob(int[] sum) {
int n = sum.length;
int[] dp = new int[n];
dp[0] = sum[0];
dp[1] = Math.max(sum[0], sum[1]);
for (int i = 2; i < n; i++) {
dp[i] = Math.max(dp[i - 2] + sum[i], dp[i - 1]);
}
return dp[n - 1];
}
执行结果