题目详情
小M最近在玩一款叫做“狼人拯救者”的游戏。在游戏中,玩家可以通过消耗金币来购买能力,这些能力会增加攻击力,但也会影响攻击速度。
小M是一个以攻击力为优先的玩家,但他必须保证自己的攻击速度不能低于0,因为攻击速度为负时将无法攻击。
现在小M面对商店中的N种能力,他拥有G枚金币和S点初始攻击速度。他想知道,在保持攻击速度大于等于0的前提下,他最多可以获得多少攻击力。商店中每种能力用三元组表示为
array[i] = [c, s, d],其中:
c表示购买该能力需要的金币数;s表示该能力对攻击速度的影响,可以为正数、零或负数;d表示该能力对攻击力的增加值。
- 在本题当中,在技能列表中遍历,我们每轮的决策在于选择或不选择该技能
- 有n项技能。
- 拥有金币G,每个技能价格为c,条件为G>=0。
- 有攻击速度S,每个技能会加上s(s>=0||s<0),条件为S>=0。
- 有攻击力D=0,每个技能会加上d(d>=0)。
- 求最高攻击力
1. 思考每轮的决策,定义状态,从而得到dp表:
对于每项技能来说,不消耗金币购买,剩余金币不变;消耗金币购买,剩余金币减少。由此可得状态定义:当前技能编号i,和剩余金币g,记为[i,g]。 状态[i,g]对应的子问题为:前i项技能在可用金币为g时所累积的最高攻击力,记为dp[i,g]。 待求解的是dp[n,G],因此需要一个尺寸为(n+1)*(G+1)的二位dp表。
2. 找出最优子结构,进而推导出状态转移方程:
- 不购买该技能:金币不变,状态变为[i-1,g]
- 购买该技能:金币减少为g-array[i][0],攻击力增加为D+array[i][2],攻击速度变为s+array[i][1]
- 最优子结构为:最大攻击力dp[i,g]等于购买技能i和不购买技能i两种方案中,攻击力最大的那一个。再加上条件攻击速度>0。
有状态转移方程为:
`if(s+array[i][1]>=0&&g>=array[i][0]){
dp[i,g]=max(dp[i-1,g]),dp[i-1,g-array[i][0]]+array[i-1][2]
}else{
dp[i,g]=dp[i-1,g]
}`
3. 确定边界条件和状态转移顺序:
当无剩余金币或者无技能列表的时候,最大攻击力为0。即首列dp[i,0],首行dp[0,g]都为0。
代码实现:
public class Main {
public static int solution(int n, int g, int s, int[][] array) {
// 定义一个二维数组 dp[i][j] 表示在前 i 种能力中,花费 j 枚金币时,能够获得的最大攻击力
int[][] dp = new int[n + 1][g + 1];
// 初始化 dp 数组,初始攻击速度为 s
int[][] speed = new int[n + 1][g + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= g; j++) {
speed[i][j] = s;
}
}
// 遍历每种能力
for (int i = 1; i <= n; i++) {
int cost = array[i - 1][0];
int speedChange = array[i - 1][1];
int damage = array[i - 1][2];
// 遍历每种可能的金币花费
for (int j = 0; j <= g; j++) {
// 不购买当前能力
dp[i][j] = dp[i - 1][j];
speed[i][j] = speed[i - 1][j];
// 如果可以购买当前能力,并且购买后攻击速度不低于0
if (j >= cost && speed[i - 1][j - cost] + speedChange >= 0) {
// 计算购买后的攻击力和攻击速度
int newDamage = dp[i - 1][j - cost] + damage;
int newSpeed = speed[i - 1][j - cost] + speedChange;
// 更新 dp 和 speed 数组
if (newDamage > dp[i][j]) {
dp[i][j] = newDamage;
speed[i][j] = newSpeed;
}
}
}
}
// 找到在所有可能的金币花费下,攻击力的最大值
int maxDamage = 0;
for (int j = 0; j <= g; j++) {
if (speed[n][j] >= 0) {
maxDamage = Math.max(maxDamage, dp[n][j]);
}
}
return maxDamage;
}
public static void main(String[] args) {
// Add your test cases here
int[][] test1 = {
{71, -51, 150},
{40, 50, 100},
{40, 50, 100},
{30, 30, 70},
{30, 30, 70},
{30, 30, 70}
};
System.out.println(solution(6, 100, 100, test1) == 240);
int[][] test2 = {
{71, -51, 150},
{40, -50, 100},
{40, -50, 100},
{30, -30, 70},
{30, -30, 70},
{30, -30, 70}
};
System.out.println(solution(6, 100, 100, test2) == 210);
}
}
头一次接触动态规划的题,之前一次也没有做过,今天头一次认真做了,也算是小小的进步了~