题目链接
问题描述
小M最近在玩一款叫做“狼人拯救者”的游戏。在游戏中,玩家可以通过消耗金币来购买能力,这些能力会增加攻击力,但也会影响攻击速度。
小M是一个以攻击力为优先的玩家,但他必须保证自己的攻击速度不能低于0,因为攻击速度为负时将无法攻击。
现在小M面对商店中的N种能力,他拥有G枚金币和S点初始攻击速度。他想知道,在保持攻击速度大于等于0的前提下,他最多可以获得多少攻击力。
商店中每种能力用三元组表示为 array[i] = [c, s, d],其中:
c表示购买该能力需要的金币数;s表示该能力对攻击速度的影响,可以为正数、零或负数;d表示该能力对攻击力的增加值。
题目分析
- 问题核心:在给定金币和初始攻击速度的情况下,选择一些能力以最大化攻击力,同时确保攻击速度不低于0。
- 输入输出:
- 输入:金币数
G,初始攻击速度S,能力列表array,每个能力由[c, s, d]表示。 - 输出:最大攻击力。
- 输入:金币数
- 约束条件:
- 攻击速度不能低于0。
- 每种能力只能购买一次。
解题思路
- 动态规划:
- 使用动态规划来解决这个问题,定义一个三维数组
dp[i][j][2],表示前i种能力中,使用不超过j枚金币时的最大攻速和最大攻击力。
- 使用动态规划来解决这个问题,定义一个三维数组
- 状态转移:
- 对于每种能力
array[k] = [c, s, d],有两种选择:- 不购买该能力:
dp[i][j] = dp[i-1][j] - 购买该能力:
dp[i][j] = [f[i - 1][j - c][0] + s, f[i - 1][j - c][1] + d],前提是j >= c且f[i - 1][j - c][0] + s >= 0,其中f[i - 1][j - c][0]是已选择能力对攻击速度的总影响。
- 不购买该能力:
- 对于每种能力
- 初始化:
dp[0][0] = [S, 0],表示没有能力时,攻速为S,攻击力为0。
- 结果:
- 最终答案为
max(dp[n][j][1]),其中n是能力的数量,j是金币数。
- 最终答案为
具体代码实现
def solution(n, G, S, array):
# 初始化dp数组,dp[i][j]表示在前i种能力中,花费j金币时能够获得的最大攻速和最大攻击力
# 初始攻速为S,攻击力为0
f = [[[S, 0] for _ in range(G + 1)] for _ in range(n + 1)]
# 遍历每种能力
for i in range(1, n + 1):
c, s, d = array[i - 1]
# 遍历金币数
for j in range(1, G + 1):
# 如果金币不足以选择当前能力,或者选择后攻速小于0,则不选择当前能力
if j < c or f[i - 1][j - c][0] + s < 0:
f[i][j] = f[i - 1][j]
continue
# 选择当前能力获得的攻击力更大
if f[i - 1][j - c][1] + d > f[i - 1][j][1]:
f[i][j] = [f[i - 1][j - c][0] + s, f[i - 1][j - c][1] + d]
# 选择当前能力获得的攻速更大
elif f[i - 1][j - c][1] + d == f[i - 1][j][1] and f[i - 1][j - c][0] + s > f[i - 1][j][0]:
f[i][j] = [f[i - 1][j - c][0] + s, f[i - 1][j - c][1] + d]
else:
# 不选择当前能力
f[i][j] = f[i - 1][j]
return max(_[1] for _ in f[n])
复杂度分析
- 时间复杂度:
O(N * G),其中N是能力的数量,G是金币数。每次计算新状态时需要遍历已选择的能力。 - 空间复杂度:
O(N * G * 2),用于存储dp数组。
总结
通过动态规划的方法,我们可以有效地解决这个问题。关键在于正确地定义状态和状态转移方程,并确保攻击速度始终不小于0。代码实现时需要注意细节,特别是攻击速度的计算。