leetcode
leetcode 601 ~ 650
删除并获得点数

删除并获得点数

难度:

标签:

题目描述

代码结果

运行时间: 36 ms, 内存: 17.7 MB


/*
  思路:
  1. 使用Java Stream来统计每个数字出现的次数。
  2. 使用IntStream.rangeClosed遍历从1到最大值的所有数字。
  3. 使用一个数组存储每个数字的最大点数。
  4. 使用mapToObj将每个数字映射成对应的最大点数,并使用reduce找到最大值。
*/

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class SolutionStream {
    public int deleteAndEarn(int[] nums) {
        if (nums == null || nums.length == 0) return 0;
        Map<Integer, Integer> countMap = Arrays.stream(nums)
                                               .boxed()
                                               .collect(Collectors.toMap(x -> x, x -> 1, Integer::sum));
        int maxVal = Arrays.stream(nums).max().orElse(0);
        int[] dp = new int[maxVal + 1];
        dp[0] = 0;
        dp[1] = countMap.getOrDefault(1, 0) * 1;
        IntStream.rangeClosed(2, maxVal).forEach(i -> {
            int earn = i * countMap.getOrDefault(i, 0);
            dp[i] = Math.max(dp[i - 1], dp[i - 2] + earn);
        });
        return dp[maxVal];
    }
}

解释

方法:

该题解使用动态规划的思路来解决问题。首先统计每个数字出现的次数,存储在 vec 数组中。然后创建一个 dp 数组,其中 dp[i] 表示考虑前 i 个数字时可以获得的最大点数。对于每个数字 i,可以选择删除它并获得 vec[i]*i 的点数,或者不删除它。如果删除它,那么就不能删除 i-1,此时的最大点数为 dp[i-2]+vec[i]*i;如果不删除它,那么最大点数为 dp[i-1]。最后返回 dp 数组的最后一个元素即为最大获得点数。

时间复杂度:

O(n+m)

空间复杂度:

O(m)

代码细节讲解

🦆
在动态规划解法中,如何确定数组`vec`和`dp`的长度以及它们的初始值是什么意义?
在动态规划解法中,数组`vec`的长度由输入数组`nums`中的最大值`max_num`确定,长度为`max_num+1`,这样可以用每个索引i直接表示数字i,方便统计每个数字出现的次数。数组`vec`的每个元素初始化为0,表示开始时每个数字出现次数为0。数组`dp`的长度同样为`max_num+1`,其用于存储到当前数字为止能获得的最大点数。`dp[0]`初始化为`vec[0]`,因为如果只有数字0,只能获得0点,`dp[1]`为`max(vec[1], vec[0])`,表示考虑数字0和1时的最优决策。
🦆
为什么在处理动态规划的状态转移时,选择`dp[i-2] + vec[i] * i`作为一种可能的状态?这种选择背后的逻辑是什么?
选择`dp[i-2] + vec[i] * i`作为状态转移的一种可能,是因为如果选择删除数字i,按题目规则,数字i-1不能被删除。因此,对于数字i的最优选择需要考虑在不包括i-1的情况下,即`dp[i-2]`的基础上加上通过删除所有的数字i获得的点数,即`vec[i] * i`。这种选择确保了删除数字i时,不违反题目中的删除规则,同时使得得分最大化。
🦆
在动态规划的实现中,`dp[1] = max(vec[1], vec[0])`这一初始化步骤的具体含义是什么?为什么不是`dp[1] = vec[1] + vec[0]`?
`dp[1] = max(vec[1], vec[0])`表示在只考虑数字0和1时的最优选择。这里的逻辑是,如果选择删除数字1,就不能删除数字0,反之亦然。因此,`dp[1]`应该取这两种选择的最大值,即删除0获得的点数`vec[0]`和删除1获得的点数`vec[1]`中的较大者。如果设置为`vec[1] + vec[0]`,则违反了题目的删除规则,即不能同时删除相邻的数字。
🦆
题解中提到如果`max_num < 2`,则返回`nums.count(1)`,这里的逻辑是否适用于所有小于2的`max_num`情况?
这里的逻辑是基于数组`nums`中的最大值`max_num`来考虑的。如果`max_num < 2`,可能的情况只有数字0和1。若`max_num`为0,则数组中只有数字0,最大点数为0。若`max_num`为1,则数组中可能有数字0和1,但最大点数来源于所有的1,即`nums.count(1)`。因此,这个逻辑只适用于`max_num`为1的情况,而不是所有小于2的情况。对于`max_num`为0的情况,应该直接返回0。

相关问题

打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

 

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

 

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400