Leetcode 740. 删除与获得点数【动态规划:以数组下标作为索引 or 以数组中的值作为索引 看数据大小关系】

问题描述

给定一个整数数组 nums ,你可以对它进行一些操作。

每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除每个等于 nums[i] - 1 或 nums[i] + 1 的元素。

开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。

解题报告

一个数删或者不删意味着拿或者不拿,当拿了这个数之后,会限制另外一些数不能拿。

我们不妨从最粗暴的方式开始。
设dp[i][0]表示第i个数不拿能获得的最大点数;dp[i][1]表示第i个数拿能获得的最大点数。这里面有两种方式创建状态方程:

  • 一个是以数组下标作为索引;
  • 一个是以数组中的值作为索引;

以数组下标作为索引

为了便于写转移方程,我们不妨先将数组排序。

  • 当第 i 个数拿时:
    如果第 i 个数和第 i-1 个数相同,那么 d p [ i ] [ 1 ] = d p [ i − 1 ] [ 1 ] + n u m s [ i ] dp[i][1]=dp[i-1][1]+nums[i] dp[i][1]=dp[i1][1]+nums[i]
    如果第 i 个数和第 i-1 个数不同,那么我们要考虑第 i 个数是否比第 i-1 个数大 1,若是,则转移方程为 d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] + n u m s [ i ] dp[i][1]=dp[i-1][0]+nums[i] dp[i][1]=dp[i1][0]+nums[i];若不是,则转移方程为 d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] ) + n u m s [ i ] dp[i][1]=max(dp[i-1][1],dp[i-1][0])+nums[i] dp[i][1]=max(dp[i1][1],dp[i1][0])+nums[i]

  • 当第 i 个数不拿时:
    如果第 i 个数和第 i-1 个数相同,那么 d p [ i ] [ 0 ] = d p [ i − 1 ] [ 0 ] dp[i][0]=dp[i-1][0] dp[i][0]=dp[i1][0]
    如果第 i 个数和第 i-1 个数不同,那么 d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] ) dp[i][0]=max(dp[i-1][1],dp[i-1][0]) dp[i][0]=max(dp[i1][1],dp[i1][0])

时间复杂度 O ( n ⋅ l o g n ) O(n\cdot logn) O(nlogn)
空间复杂度 O ( n ) O(n) O(n)

以数组中的值作为索引

以数组中的值作为索引时,第i个数和第i-1个数具有天然的+1性能,所以可以省去很多判断。

转移方程为:
d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] + i ∗ c o u n t ( i ) dp[i][1]=dp[i-1][0]+i*count(i) dp[i][1]=dp[i1][0]+icount(i)
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] ) dp[i][0]=max(dp[i-1][0],dp[i-1][1]) dp[i][0]=max(dp[i1][0],dp[i1][1])

时间复杂度 O ( m a x ( n u m [ i ] ) ) O(max(num[i])) O(max(num[i]))
空间复杂度 O ( m a x ( n u m [ i ] ) ) O(max(num[i])) O(max(num[i]))

这样的转移方程还有很多优化空间,具体见:Leetcode 198. 打家劫舍【动态规划,一步步从最粗暴的动态规划到最优化滚动数组实现】

实现代码

以数组下标作为索引实现

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        int n= nums.size();
        if(n==0) return 0;
        vector<vector<int>>dp(n,vector<int>(2,0));
        sort(nums.begin(), nums.end());
        dp[0][0]=0;
        dp[0][1]=nums[0];
        for(int i=1;i<n;i++){
            if(nums[i]-nums[i-1]==0){
                dp[i][0]=dp[i-1][0];
                dp[i][1]=dp[i-1][1]+nums[i];
                continue;
            }
            if(nums[i]-nums[i-1]==1){
                dp[i][1]=dp[i-1][0]+nums[i];
            }
            else{
                dp[i][1]=max(dp[i-1][1],dp[i-1][0])+nums[i];
            }
            dp[i][0]=max(dp[i-1][1],dp[i-1][0]);
        }
        return max(dp[n-1][0],dp[n-1][1]);
    }
};

以数组中的值作为索引实现

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        if (nums.empty()) return 0;
        int N = *max_element(nums.begin(), nums.end());
        vector<int> counts(N + 1, 0);
        for (auto x : nums) {
            ++counts[x];
        }
        vector<vector<int>> dp(N + 1, vector<int>(2,0));
        dp[1][0] = 0;
        dp[1][1]=counts[1];
        for (int i = 2; i <= N; ++i) {
            dp[i][0]=max(dp[i-1][1],dp[i-1][0]);
            dp[i][1]=dp[i-1][0]+i*counts[i];
        }
        return max(dp[N][0],dp[N][1]);
    }
};

总结

这种是以数组中的值作为索引还是以数组下标作为索引,取决于数组的大小和数组中最大值的大小关系。从这道题的所给数据范围来看【nums的长度最大为20000。每个整数nums[i]的大小都在[1, 10000]范围内】,明显是以数组中的值作为索引的时间复杂度和空间复杂度来的小。

参考资料

[1] Leetcode 740. 删除与获得点数
[2] Leetcode 198. 打家劫舍【动态规划,一步步从最粗暴的动态规划到最优化滚动数组实现】

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值