泠弦月屿 Rinzemoon
← Back to the beginning

LeetCode740:获得点数

Leetcode
2026-07-02 文章 泠時月 2 分钟 568 字
算法
文件路径: content/posts/LeetCode740.md

LeetCode740 删除并获得点数(Medium)

题目叙述:

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

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

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

示例 1:

code
输入:nums = [3,4,2]
输出:6
解释:
你可以执行下列步骤:
- 删除 4 获得 4 个点数,因此 3 也被删除。nums = [2]。
- 之后,删除 2 获得 2 个点数。nums = []。
总共获得 6 个点数。

示例 2:

code
输入:nums = [2,2,3,3,3,4]
输出:9
解释:
你可以执行下列步骤:
- 删除 3 获得 3 个点数。所有的 2  4 也被删除。nums = [3,3]。
- 之后,再次删除 3 获得 3 个点数。nums = [3]。
- 再次删除 3 获得 3 个点数。nums = []。
总共获得 9 个点数。

提示:

  • 1 <= nums.length <= 2 * 104
  • 1 <= nums[i] <= 104

题解

cpp
class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        if (nums.empty()) return 0;

        int maxVal = *max_element(nums.begin(), nums.end());
        vector<int> sum(maxVal + 1, 0);
        for (int x : nums) {
            sum[x] += x;
        }

        // 定义 dp[i]:只考虑数字 0 到 i,能获得的最大点数
        // dp[0] = 0(数字0不存在)
        // dp[1] = sum[1](只有一个数字1时,拿了就是它的总分)
        vector<int> dp(maxVal + 1, 0);
        dp[1] = sum[1];

        for (int i = 2; i <= maxVal; ++i) {
            // 情况1:不拿 i -> 结果就是 dp[i-1]
            // 情况2:拿 i -> i-1 必须删除,所以只能看 i-2 的结果,再加 i 的分
            dp[i] = max(dp[i - 1], dp[i - 2] + sum[i]);
        }
        return dp[maxVal];
    }
};

流程

首先是看函数Body,给了一个Vector<int> nums

  • 首先是判空。nums.size != 0

  • 找到nums里的最大值 去维护一个数组。

为什么要用nums数组的最大值去维护数组呢,我们采用这样的方式:

  • vector<int> sum 的 下标即为值,然后sum[idx]即为idx 的总和。
cpp
// 也就是这样
vector<int> sum(maxVal + 1, 0);
        for (int x : nums) {
            sum[x] += x;   // 相同值累加,因为选了 x 就能拿走所有 x
        }

这样,我们就拿到了一个sum的vector,其核心是存储对应下标对应的Credit,它根据index进行归一化,把Credit * 次数来对应sum[idx]的值。

接下来就是应用经典的打家劫舍思路进行动态规划处理。

题解里有一个注释值得分析

cpp
// 定义 dp[i]:只考虑数字 0 到 i,能获得的最大点数
        // dp[0] = 0(数字0不存在)
        // dp[1] = sum[1](只有一个数字1时,拿了就是它的总分)

这就是打家劫舍里的选或不选的问题,先遍历sum数组。最优值)。

cpp
vector<int> dp(maxVal + 1, 0);
dp[1] = sum[1];
for (int i = 2; i <= maxVal; ++i) {
            // 情况1:不拿 i -> 结果就是 dp[i-1]
            // 情况2:拿 i -> i-1 必须删除,所以只能看 i-2 的结果,再加 i 的分
    dp[i] = max(dp[i - 1], dp[i - 2] + sum[i]);
    			// 不拿i    //拿了i,也就是加sum[i],同时也略过了i - 1
}
        return dp[maxVal];

现在我们来看for以及for前的处理。

  • 定义 dp[i]:只考虑数字 0 到 i,能获得的最大点数,先把初始状态赋值给sum[1],也就是dp[1]

然后根据for循环,从index = 2 到index <= maxVal,每次循环更新都动态判断max(dp[i - 1], dp[i - 2] + sum[i])

当然决定拿还是不拿由std::max决定,毕竟我们的目标是获得最大点数。

一次循环之后填充了dp,自然最后dp[maxVal] 是我们获得到的最大值。

其实它的思想精髓在迭代,我们从sum的数组得到了每个值对应的总Credit,然后遍历sum数组进行动态规划。

cpp
// dp[0] = 0(数字0不存在)
// dp[1] = sum[1](只有一个数字1时,拿了就是它的总分)

用这两个状态来维护初始值,因为sum的index是nums的值,不是单纯的index。 也就是前面说的 定义 dp[i]:只考虑数字 0 到 i,能获得的最大点数

所以在i + 1,i + 2 i + … 的时候,都会参考到前面的结果动态来判断全局的最大Credit。

© 泠時月 2026,采用 CC BY 4.0 许可,转载保留署名。

留言 · 0 段对话

扫码分享

二维码