题目:
题目理解:
玩一个游戏,有 n 个英雄,每个英雄初始能力值都是 1。你可以最多升级 k 次,每次升级可以任选一个英雄,并选择一个正整数 x,将该英雄的能力值 a 变为 a + floor(a/x) (floor 表示向下取整)。
每个英雄都有一个目标能力值 b 和对应的奖励 c。当一个英雄的能力值第一次达到或超过其目标能力值 b 时,你就能获得奖励 c。每个英雄的奖励只能获得一次。
问:如何选择升级英雄和 x 的值,才能使获得的总奖励最大?
思路讲解
这道题可以用动态规划来解决。核心思想是考虑前 i 个英雄,在最多使用 j 次升级机会的情况下,能够获得的最大奖励。
-
状态定义: dp[i][j] 表示考虑前 i 个英雄,使用最多 j 次升级机会,能获得的最大奖励。
-
状态转移方程:
- 不升级第 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 时,才能进行这样的升级。
-
边界条件: dp[0][j] = 0 (没有英雄,奖励为 0)
代码:
代码解析
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 函数则是经典的二维动态规划实现。