青训营X豆包MarsCode技术训练|豆包MarsCode AI 刷题

37 阅读3分钟

题目: ​​​ 题目理解: 玩一个游戏,有 n 个英雄,每个英雄初始能力值都是 1。你可以最多升级 k 次,每次升级可以任选一个英雄,并选择一个正整数 x,将该英雄的能力值 a 变为 a + floor(a/x) (floor 表示向下取整)。

每个英雄都有一个目标能力值 b 和对应的奖励 c。当一个英雄的能力值第一次达到或超过其目标能力值 b 时,你就能获得奖励 c。每个英雄的奖励只能获得一次。

问:如何选择升级英雄和 x 的值,才能使获得的总奖励最大?

思路讲解

这道题可以用动态规划来解决。核心思想是考虑前 i 个英雄,在最多使用 j 次升级机会的情况下,能够获得的最大奖励。

  1. 状态定义:  dp[i][j] 表示考虑前 i 个英雄,使用最多 j 次升级机会,能获得的最大奖励。

  2. 状态转移方程:

    • 不升级第 i 个英雄:  dp[i][j] = dp[i-1][j] (继承前 i-1 个英雄的结果)
    • 升级第 i 个英雄:  dp[i][j] = max(dp[i][j], dp[i-1][j - cost] + c[i-1])
      其中 cost 是将第 i 个英雄升级到目标能力值 b[i-1] 所需的最小升级次数。 只有当 j >= cost 且 cost <=k 时,才能进行这样的升级。
  3. 边界条件:  dp[0][j] = 0 (没有英雄,奖励为 0)

代码:

image.png

image.png

代码解析

int get_cost(int initial_value, int target_value, int k) {
    // 计算将初始值 initial_value 升级到目标值 target_value 所需的最小升级次数
    // 如果升级次数超过 k,则返回 k+1,表示无法在 k 次内达到目标值

    if (initial_value >= target_value) {
        return 0; // 已达到目标值,无需升级
    }

    vector<int> dp(target_value + 1, k + 1); // dp[i] 表示达到能力值 i 所需的最小升级次数, 初始化为 k+1 (不可达)
    dp[initial_value] = 0; // 初始值对应的升级次数为 0

    for (int i = initial_value; i <= target_value; ++i) { 
        if (dp[i] > k) continue; // 当前能力值不可达,跳过

        for (int x = 1; x <= i; ++x) { // 遍历所有可能的 x 值
            int next_value = i + i / x; // 计算升级后的能力值
            if (next_value <= target_value) {
                dp[next_value] = min(dp[next_value], dp[i] + 1); // 更新达到 next_value 的最小升级次数
            }
        }
    }

    return (dp[target_value] > k) ? k + 1 : dp[target_value]; // 返回达到目标值所需的最小升级次数,或 k+1 (表示不可达)
}

int solution(int n, int k, const vector<int>& b, const vector<int>& c) {
    // 计算最多升级 k 次,能获得的最大奖励

    vector<vector<int>> dp(n + 1, vector<int>(k + 1, 0)); // dp[i][j]:考虑前 i 个英雄,使用最多 j 次升级机会,能获得的最大奖励

    for (int i = 1; i <= n; ++i) { // 遍历每个英雄
        int cost = get_cost(1, b[i - 1], k); // 计算升级第 i 个英雄到目标值所需的最小升级次数
        for (int j = 0; j <= k; ++j) { // 遍历所有可能的升级次数
            dp[i][j] = dp[i - 1][j]; // 不升级第 i 个英雄
            if (j >= cost && cost <= k) { // 可以升级第 i 个英雄,且升级次数不超过 k
                dp[i][j] = max(dp[i][j], dp[i - 1][j - cost] + c[i - 1]); // 升级第 i 个英雄
            }
        }
    }

    return dp[n][k]; // 返回最终结果
}
```get_cost 函数使用了动态规划来计算升级所需的最小次数,避免了简单的贪心策略可能导致的错误。 solution 函数则是经典的二维动态规划实现。