给你一个整数数组 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] <= 10
思路:
做过打家劫舍(动态规划)的可能会更熟悉这道题,这道题就是它的变形,让我们先来看看打家劫舍:
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。
思路:
这是一个递归的想法,假设有i家人等着你去偷
1、不偷窃最后一间房间,则偷窃前i-1间房间,问题就转化为了偷窃1号到i - 1号房间所能获得的max金额,即dp[i] = dp[i-1] 。
2、偷窃最后一间房间,问题就转化为了偷窃1号到i - 2号房间所能获得的max金额 + 偷窃第i号房间的金额,即dp[i] = dp[i - 2] + nums[i]。
3、然后dp[i+1]=max(dp[i],dp[i-1]+nums[i]) 取两种情况的最大值,并且让dp[1]=nums[0],dp[0]=0
打家劫舍代码:
int rob(int* nums, int numsSize){
int i;
int dp[numsSize+1];
if(numsSize==1)
return nums[0];
else{
dp[0]=0;
dp[1]=nums[0];
for(i=2;i<=numsSize;i++)
{
dp[i] =fmax(dp[i-1], dp[i - 2] + nums[i - 1]); //状态转移方程
}
return dp[numsSize];
}
}
4、删除并获得点数,我们可以把点数储存在一个数组里面:
比如F[2,3,2,3,3,4,6,4]
第一步:存放在新数组S中就形成[0,0,4,9,8,0,6],比如S[2]=i(也就是2)*2(就是2的数量)= 4
注意新开的数组大小需要满足条件10的4次方
下面这个代码就可以实现让其构造成打家劫舍模式
for(i=0;i<numsSize;i++)
{
f[nums[i]]+=nums[i];//直接利用公式存进去
}
第二步:套用1、2、3思路解出答案
代码如下:
int deleteAndEarn(int* nums, int numsSize){
int f[10001]={0};
int i;
for(i=0;i<numsSize;i++)
{
f[nums[i]]+=nums[i];
}
for(i=2;i<10001;i++)
{
f[i]=f[i-1]>f[i-2]+f[i]?f[i-1]:f[i-2]+f[i];//三元条件运算符
}
return f[10000];
}
时间复杂度O(n+1000):
实际我们还可以优化一点,找出nums[i]的最大值,让其作为循环的边界,这也也就避免了重复运算,最后时间复杂度变为o(n+max(nums[i]))。
空间复杂度o(max(nums[i])):
优化就是先找出num[i]的max,然后我们定义一个max大小的数组。