DP练习题 植物大战僵尸 题解 动态规划+费用提前计算
题目描述
聪聪喜欢玩植物大战僵尸,在游戏里有一条水平道路,道路的一端是入口,另一端是房子。僵尸会从道路的入口一端向房子一端移动。这条道路刚好穿过 \(N\) 块连续的空地。初始时,僵尸通过每块空地的时间是 \(T\) 秒。玩家可以在这 \(N\) 个空地中种植植物以攻击经过的僵尸,每块空地中只能种植一种植物。
共有三种不同类型的植物,分别是红草、蓝草和绿草,作用分别是攻击、减速以及下毒。每种植物只能在僵尸通过它所在空地的这段时间内攻击到僵尸。
- 当僵尸经过一块红草所在的空地时,每秒钟生命值会减少 \(R\) 点;
- 当僵尸从一块蓝草所在的空地走出之后,通过每块空地的时间延长 \(B\) 秒;
- 当僵尸从一块绿草所在的空地走出之后,每秒钟会因中毒减少 \(G\) 点生命值。
蓝草的减速效果和绿草的下毒效果是可以累加的。也就是说,
- 僵尸通过 \(n\) 块蓝草所在的空地之后,它通过每块空地的时间会变成 \(T+B \times n\) 秒;
- 僵尸通过 \(n\) 块绿草所在的空地之后,它每秒钟会因中毒失去 \(G \times n\) 点生命值。
注:减速和中毒效果会一直持续下去。
聪聪想知道:怎样在这 \(N\) 块空地里种植各种类型的植物,才能使通过的僵尸失去的生命值最大。输出这个最大值。
输入格式
一行,五个空格隔开的整数 \(N\)、\(R\)、\(G\)、\(B\)、\(T\) 。
输出格式
一行,一个整数,即通过的僵尸失去的最大的生命值。
样例输入
3 3 3 3 3
样例输出
45
说明/提示
输入保证 \(N \le 3000\),\(R,G,B,T\) 均为不超过 \(100\) 的正整数。
题解
首先,\(N\) 块空地必须得都种上草,不然就浪费了。
其次,造成伤害的只有红草,有持续效果的是蓝草和绿草,所以为了让伤害尽可能地高,肯定是蓝草和绿草放前面,后面是连续的红草。
但是蓝草和绿草的放置顺序就没有办法直接确定了,所以我们可以定义状态 \(f[i][j]\),表示前 \(i+j\) 块草地中,有 \(i\) 块蓝草和 \(j\) 块绿草的情况下,僵尸所失去的最大生命值(不包括后面的红草所带来的杀伤值)。
对于每一个状态 \(f[i][j]\),它只有可能从以下两种状态转化而来:
- 前 \(i+j-1\) 块草坪中,有 \(i\) 块蓝草和 \(j-1\) 块绿草,第 \(i+j\) 块草坪种绿草,此时僵尸经过第 \(i+j\) 块草坪造成的伤害的损耗为 \((i \times B + T) \times G \times (j - 1)\);
- 前 \(i+j-1\) 块草坪中,有 \(i-1\) 块蓝草和 \(j\) 块绿草,第 \(i+j\) 块草坪种蓝草,此时僵尸经过第 \(i+j\) 块草坪造成的伤害的损耗为 \(((i-1) \times B + T) \times G \times j\)。
所以
前 \(i+j\) 块草地中有 \(i\) 块种蓝草,\(j\) 块种绿草的情况下,对于剩余 \(n-i-j\) 块种红草的草坪来说:
- 经过每块红草的时间都为 \((i \times B + T\);
- 经过每块红草收到的伤害是红草的物理伤害加上累积的毒伤 \(R + j \times G\)
所以经过后 \(n-i-j\) 块草坪的总伤害为
所以最终的答案为
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3030;
int n;
long long R, G, B, T, f[maxn][maxn], ans;
int main() {
cin >> n >> R >> G >> B >> T;
for (int i = 0; i <= n; i ++) {
for (int j = 0; i+j <= n; j ++) {
if (j) f[i][j] = max(f[i][j], f[i][j-1] + (i*B+T)*G*(j-1));
if (i) f[i][j] = max(f[i][j], f[i-1][j] + ((i-1)*B+T)*G*j);
ans = max(ans, f[i][j] + (n-i-j) * (i*B+T) * (R+j*G));
}
}
cout << ans << endl;
return 0;
}