Problem | 植物大战僵尸(mtower.cpp/c/pas) | |
题目描述 | 何老板喜欢玩植物大战僵尸,在游戏里有一条水平道路,道路的一端是入口,另一端是房子。僵尸会从道路的入口一端向房子一端移动。这条道路刚好穿过N块连续的空地。初始时,僵尸通过每块空地的时间是T秒。玩家可以在这N个空地中种植植物以攻击经过的僵尸,每块空地中只能种植一种植物。 共有三种不同类型的植物,分别是红草、蓝草和绿草,作用分别是攻击、减速以及下毒。每种植物只能在僵尸通过它所在空地的这段时间内攻击到僵尸。 当僵尸经过一块红草所在的空地时,每秒钟生命值会减少R点;当僵尸从一块蓝草所在的空地走出之后,通过每块空地的时间延长B秒;当僵尸从一块绿草所在的空地走出之后,每秒钟会因中毒减少G点生命值。蓝草的减速效果和绿草的下毒效果是可以累加的。也就是说,僵尸通过n块蓝草所在的空地之后,它通过每块空地的时间会变成T+B*n秒;僵尸通过n块绿草所在的空地之后,它每秒钟会因中毒失去G*n点生命值。注:减速和中毒效果会一直持续下去 何老板想知道:怎样在这N块空地里种植各种类型的植物,才能使通过的僵尸失去的生命值最大。输出这个最大值。 | |
输入格式 | 一行,五个空格隔开的整数N、R、G、B、T。 | |
输出格式 | 一行,一个整数,即通过的僵尸失去的最大的生命值。 | |
输入样例 | 5 4 3 2 1 | |
输出样例 | 82 | |
数据范围 | 20%的数据满足N<=12。 50%的数据满足N<=100。 100%的数据满足1<=N<=2000; 0 <= R, G, B <= 65536; 0 <= T <= 3。 |
这道题的难点主要在于想到这是一个动规题
经过分析,我们知道这是动规之后,就开始思考状态转移方程了
首先我们应该明白,红草放在最后的时候,我们得到的解才可能是最优的,因为红草的攻击是暂时性的,而蓝草和绿草的攻击都是持续性的
我们设最后k个位置放的是红草,那么前left=n-k个位置就是剩下的绿草和蓝草放的位置
我们用f[i][j]来表示前i+j个位置中绿草放i个,蓝草放j个的最大伤害
而求解f[i][j]实际上是在对第i+j个格子进行讨论
那么对于f[i][j]来说只有两种决策,要么放绿草,要么放蓝草
因此方程如下
f[i][j]=max {f[i1][j]+(T+i*B)*(j-1)*G; 表示最后一个位置放绿草,通过最后一格的时间是T+bb*B,中毒造成的伤害是每时间(gg-1)*G
f[i][j-1]+(T+(i-1)*B)*j*G; } 表示最后一个位置放蓝草,通过最后一格的时间是T+(bb-1)*B,中毒造成的伤害是每时gg*G
对于每个f[i][j],加上最后的k个红草带来的伤害,以及在红草的区域由于中毒带来的伤害(后者易忽视),找到最优值即可
ans= max{f[i][j]+k*(T+i*B)*(R+j*G)}
由于题目中限制了空间只有3m,因此要用滚动数组优化
#include<iostream>
#define LL long long
using namespace std;
const LL inf=2e9;
LL N,G,B,T,R;
LL f[3][3];
int main(){
cin>>N>>R>>G>>B>>T;
LL i,j,k,ans=0;
for(k=N;k>=1;k--){//注意,根据状态转移方程特征,这里的K应该逆序枚举
LL left=N-k;
for(i=0;i<=left;i++){
LL j=left-i;
if(i>0)
f[i&1][j&1]=max(f[i&1][j&1],f[(i-1)&1][j&1]+(T+j*B)*G*(i-1));
if(j>0)
f[i&1][j&1]=max(f[i&1][j&1],f[i&1][(j-1)&1]+(T+(j-1)*B)*G*i);
ans=max(ans,f[i&1][j&1]+k*(T+j*B)*(R+i*G));
}
}
cout<<ans;
}