leetcode740. 删除并获得点数(线性相邻dp问题)

Posted 卷王2048

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode740. 删除并获得点数(线性相邻dp问题)相关的知识,希望对你有一定的参考价值。

740. 删除并获得点数

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

每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除每个等于 nums[i] - 1nums[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个数

  1. 不删除第i个数:对于这个数来说没有获得点数,总点数和上一个数一样dp[i] = dp[i-1]
  2. 删除第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++动态规划)

LeetCode 740 删除并获得点数 HERODING的LeetCode之路

740. 删除并获得点数(dp)

740. 删除并获得点数(dp)

740. 删除并获得点数

740. 删除并获得点数