题意:A代表你的初始攻击力,D代表你每回合递增攻击力,A,D初始为0。
第一行给一个t,代表t组测试数据
给你N个回合(n<=100),第i个回合有3个数字a[i],b[i],c[i](范围均1e9),代表有三种选择,1.攻击,造成A+a[i]的伤害
2.使D增加b[i]
3.使A增加c[i]
问,经过N回合后最多可以造成多少伤害
样例如下。。
一眼看出是个DP,但是不好推。一开始在想DP存什么,怎么开数组,因为当前造成的伤害,A,D的数量级都是1e9.又不能贪心证明当前的三种选择哪一种最优。想了一个小时快自闭才发现倒推。。实属DD。
想出倒推之后转化题意,倒推的过程中是不知道现在的A和D是多少的。那么我只用管造成的伤害就行了。设当前回合为第i回合。如果选择攻击,那么造成伤害+a[i],假设当前回合选择使A增加c[i],那么在第i-n的回合中每一次进攻造成的伤害就会增加c[i],倒推过程中,假设之前进攻了k次,并且当前回合选择了把A增加c[i],那么伤害就会增加k*c[i]。同理,如果当前回合选择使D增加b[i],那么如果在第q个回合攻击,伤害就会增加(q-i)*b[i],这是进攻一次的情况。那么如果这个回合之后进攻了k次,那么每次伤害会增加(q1+q2+...+qk - k*i )*b[i],令之前那些q的总和为j,那么伤害就会增加(j-i*k)*b[i]。这里的j指“倒推过程中,之前进攻的回合数之和”。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define last (i+1)%2
#define now i%2
ll dp[2][105][5051];//第一维代表当前回合,由于害怕空间不够,用滚动数组和上面define对应,
//第二维代表之前进攻次数。5051为进攻回合数之和(从1加到100最多5050)
ll a[105],b[105],c[105],n,t;
int main(){
cin>>t;
while(t--){
cin>>n;
ll ans=0;
memset(dp,0,sizeof dp);
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i]>>c[i];
dp[n%2][1][n]=a[n];//第n回合肯定会进攻,进攻次数=1,进攻回合总和=n,伤害为a[n]
for(int i=n-1;i>=1;i--){//倒推枚举回合数
for(int k=n-i;k>=1;k--){//倒推枚举攻击次数
ll minn=(i+i+k-1)*k/2,maxx=(n+n-k+1)*k/2;//枚举上界和下界,从1到5050应该也行,相当于剪枝
for(int j=maxx;j>=minn;j--){//倒推枚举回合数之和
dp[now][k+1][j+i]=max(dp[now][k+1][j+i],dp[last][k][j]+a[i]);//选择进攻,递推状态
dp[now][k][j]=max(dp[now][k][j],dp[last][k][j]+max(k*c[i],(j-k*i)*b[i]));//不进攻,k,j不变,但是造成伤害增加
}
}
for(int k=n-i;k>=1;k--)
for(int j=(n+n-k+1)*k/2;j>=(i+i+k-1)*k/2;j--)
dp[last][k][j]=0;//滚动数组用完及时清空
}
for(int k=1;k<=n;k++)
for(int j=1;j<=5050;j++)
ans=max(ans,dp[1][k][j]);//枚举k,j。选择递推到1时的最大值
cout<<ans<<endl;
}
}
这么看来并不是很难,然而现场推了快两个小时还wa2发,罚时差1h银尾,好菜啊。QAQ