执行操作可获得的最大总奖励I

2024-10-25

题目:

给你一个整数数组 rewardValues,长度为 n,代表奖励的值。

最初,你的总奖励 x 为 0,所有下标都是 未标记 的。你可以执行以下操作 任意次

  • 从区间 [0, n - 1] 中选择一个 未标记 的下标 i
  • 如果 rewardValues[i] 大于 你当前的总奖励 x,则将 rewardValues[i] 加到 x 上(即 x = x + rewardValues[i]),并 标记 下标 i

以整数形式返回执行最优操作能够获得的 最大 总奖励。

示例 1:

输入:rewardValues = [1,1,3,3]

输出:4

解释:

依次标记下标 0 和 2,总奖励为 4,这是可获得的最大值。

示例 2:

输入:rewardValues = [1,6,4,3,2]

输出:11

解释:

依次标记下标 0、2 和 1。总奖励为 11,这是可获得的最大值。

提示:

  • 1 <= rewardValues.length <= 2000
  • 1 <= rewardValues[i] <= 2000

思路:

记 rewardValues 的最大值为 m,因为最后一次操作前的总奖励一定小于等于 m − 1,所以可获得的最大总奖励小于等于 2m − 1。假设上一次操作选择的奖励值为 x1,那么执行操作后的总奖励 x ≥ x1,根据题意,后面任一操作选择的奖励值 x2 一定都大于 x,从而有 x2 > x1,因此执行的操作是按照奖励值单调递增的。

根据以上推断,首先将 rewardValues 从小到大进行排序,使用 dp[k] 表示总奖励 k 是否可获得,初始时 dp[0] = 1,表示不执行任何操作获得总奖励 0。然后我们对 rewardValues 进行遍历,令当前值为 x,那么对于 k∈[x, 2x − 1](将 k 倒序枚举),将 dp[k] 更新为 dp[k − x] ∣ dp[k](符号 ∣ 表示或操作),表示先前的操作可以获得总奖励 k − x,那么加上 x 后,就可以获取总奖励 k。最后返回 dp 中可以获得的最大总奖励。

代码:

class Solution {
public:
    int maxTotalReward(vector<int>& rewardValues) {
        sort(rewardValues.begin(), rewardValues.end());
        int m = rewardValues.back();
        vector<int> dp(2 * m);
        dp[0] = 1;
        for (int x : rewardValues) {
            for (int k = 2 * x - 1; k >= x; k--) {
                if (dp[k - x]) dp[k] = 1;
            }
        }
        int res = 0;
        for (int i = 0; i < dp.size(); i++) {
            if (dp[i]) res = i;
        }
        return res;
    }
};