leetcode740. 删除并获得点数(线性相邻dp问题)
Posted 卷王2048
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode740. 删除并获得点数(线性相邻dp问题)相关的知识,希望对你有一定的参考价值。
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
思路
集合:删除的数是第i个数
属性:获得总点数的最大值
集合划分:是否删除第i个数
- 不删除第i个数:对于这个数来说没有获得点数,总点数和上一个数一样
dp[i] = dp[i-1]
- 删除第i个数:因为删除第
i
个数的同时也要删去第i-1
个,此时的总点数就是第i-2
个数的点数,再加上当前点数。因为这是去重后的结果,实际上如果有n
个第i
个数,当前点数的总价值实际为n*num[i]
所以有dp[i] = dp[i-2]+n*num[i]
要想实现以上状态转移,先要给数组去重,并统计每个数出现的次数
题解
方法1
为了节省去重
这一步,获得最简单的代码,我们选择对数据范围内的所有数进行枚举
import java.util.Arrays;
/**
* @see 线性dp https://leetcode-cn.com/problems/delete-and-earn/
* 数据范围
* 1 <= nums.length <= 20000
* 1 <= nums[i] <= 10000
*/
public class deleteAndEarnClass {
/**
* 集合:删除的数是第i个数
* 属性:获得点数的最大值
* 集合划分:是否删除第i个数
* 要想实现以上状态转移,先要给数组去重,并统计每个数出现的次数
* @param nums
* @return
*/
public static int deleteAndEarn(int[] nums) {
//枚举数据范围内的所有数
int[] dp = new int[10010];
int[] count = new int[10010];
for (int num : nums) {
count[num]++;
}
//dp[0] 表示删除0,在本题中无意义
dp[1] = count[1];
for(int i = 2; i <10001; i++) {
dp[i] = Math.max(dp[i-1],dp[i-2]+i*count[i]);
}
return dp[10000];
}
public static void main(String[] args) {
int[]arr = {2,2,3,4};
System.out.println(deleteAndEarn(arr));
}
}
方法二(滚动数组优化)
在朴素版的基础上,我们发现,dp[i]
的值值和dp[i-1]
和dp[i-2]
有关,所以我们没有必要开dp数组,只需要用2个值存下dp[i-1]
和dp[i-2]
并不断更新这两个值即可。这种思想叫滚动数组
且我们在实现去重
和统计个数
时,可以用第i个数的总价值
来替代优化
import java.util.Arrays;
public class deleteAndEarnOptimizationClass {
/**
* 集合:删除的数是第i个数
* 属性:获得点数的最大值
* 集合划分:是否删除第i个数
* 要想实现以上状态转移,先要给数组去重,并统计每个数出现的次数
* 滚动数组优化版
* @param arr
* @return
*/
public static int deleteAndEarn(int[] arr) {
int manVal = Arrays.stream(arr).max().getAsInt();
// int[] dp = new int[manVal+1];
int[] count = new int[manVal+1];
//这里把“先要给数组去重,并统计每个数出现的次数”这个思路优化为“统计每个数的总价值”的数组
for (int num : arr) {
count[num]+= num;
}
// 在朴素版的基础上,我们发现,dp[i]的值值和dp[i-1]和dp[i-2]有关
// 所以我们没有必要开dp数组,只需要用2个值存下dp[i-1]和dp[i-2]
// 并不断更新这两个值即可
// 这种思想叫滚动数组
int beforeBehind = count[0], behind = Math.max(count[0],count[1]);
for(int i = 2; i <manVal+1; i++) {
int temp = behind;
behind = Math.max(beforeBehind+count[i],behind);
beforeBehind = temp;
}
return behind;
}
public static void main(String[] args) {
int[]arr = {2,2,3,4};
System.out.println(deleteAndEarn(arr));
}
}
以上是关于leetcode740. 删除并获得点数(线性相邻dp问题)的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode740. 删除并获得点数(java/c++动态规划)