Leetcode 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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/delete-and-earn
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


思路

基本思路还是 198. 打家劫舍 。首先遍历一遍数组,生成一张哈希表,统计每种数字的出现次数。这里和 198 题的区别就在于,动态转移方程要根据前后两个数字之间相邻与否来分类讨论。动态规划数组的意义:dp[i] 表示在前 i 小的数字中操作得到的最大点数(为了简化说明,数组从 1 开始计数)

举例:如果 3 出现了 1 次,4 出现了 2 次,5 出现了 3 次,7 出现了 4 次,首先初始化 dp 的前两个元素,数字 3 是第 1 小的元素,显然 d p [ 1 ] = 3 ∗ 1 = 3 dp[1] = 3 * 1 = 3 dp[1]=31=3,到了数字 4,第 2 小的元素,此时 d p [ 2 ] = m a x ( d p [ 1 ] , 4 ∗ 2 ) = 8 dp[2] = max(dp[1],4*2)=8 dp[2]=max(dp[1],42)=8

到了数字 5,第 3 小的元素,由于 4 和 5 是相邻的,相当于打家劫舍中相邻的屋子,两间屋子只能抢一间,那么转移方程就是 d p [ i ] = m a x ( d p [ i − 2 ] + 5 ∗ 3 , d p [ i − 1 ] ) dp[i]=max(dp[i−2]+5*3,dp[i−1]) dp[i]=max(dp[i2]+53,dp[i1])

到了数字 7,由于 5 和 7 是不相邻的,也就没有这个限制,所以此时数字 7 的点数是必拿,不需要讨论的,在 7 之前还要拿最多的点数,那么转移方程就是 d p [ i ] = 7 ∗ 4 + m a x ( d p [ i − 2 ] , d p [ i − 1 ] ) dp[i]=7*4+max(dp[i−2],dp[i−1]) dp[i]=74+max(dp[i2],dp[i1])

下面代码中用了 map 来统计数字出现的次数,做完之后才发现数据范围 1 <= nums[i] <= 104,所以用数组统计就可以了。

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

C++ 代码

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        map<int, int> numToProduct;
        for (const int num : nums)
            numToProduct[num] += num;
        
        vector<int> dp(numToProduct.size());
        auto it = numToProduct.begin();
        dp[0] = it->second;
        if (numToProduct.size() == 1)
            return dp[0];

        auto last = it;
        ++it;
        if (last->first + 1 == it->first)
            dp[1] = max(last->second, it->second);
        else
            dp[1] = last->second + it->second;
        if (numToProduct.size() == 2)
            return dp[1];
        
        int dpIndex = 2;
        for (++it; it != numToProduct.end(); ++it, ++dpIndex) {
            // printf("first = %d, second = %d\n", it->first, it->second);
            auto last = it;
            --last;
            if (last->first + 1 == it->first)
                dp[dpIndex] = max(dp[dpIndex - 2] + it->second, dp[dpIndex - 1]);
            else
                dp[dpIndex] = max(dp[dpIndex - 1], dp[dpIndex - 2]) + it->second;
        }
        return dp.back();
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值