题目链接:https://codeforces.com/gym/102056/problem/I
题目大意:一共有n轮游戏,有A和D,A是每一轮的基础攻击值,D是每一轮基础攻击值增加的值,每一轮可以有三个操作,第一个是发动攻击,可以在原来攻击的基础上得到ai增幅,第二个是增加D,第三个是增加A,问最大攻击量
题目思路:首先可以发现,只有操作1才是真正能够打出伤害对答案做出贡献的,但他的ai是只有自己能用别人不能用,而操作2的贡献会随着时间的流逝水涨船高,操作3能直接提高A的能力,所以每个操作能对答案的影响都是各有千秋,不能直接贪心,所以就要考虑DP。
观察操作1,ai是一次性的,没啥好关注的,操作2,每次都可以水涨船高,所以如果将来有一个j发动攻击,那么这次增幅为最终答案做出的贡献就是(j-i)*b[i],由此就可以发现,本质上操作2所有的贡献可以理解成, 后面所有攻击的下标和-攻击次数*i,就是第i次选择第二次操作所得到的结果,操作3是直接增加攻击力,那么将来所有的攻击都能得到增幅,若接下来攻击次数是cnt,这次选操作三对答案的贡献就是cnt*c[i]。
所以我们需要知道的仅仅只是接下来的攻击次数和攻击的下标和就行,开两维来记录它,由于还需要记录上一轮情况,所以还有一维记录轮数。
知道这里dp方程就已经出来了
dp的本质是通过已知得到未知,什么状态是我们已知的?bingo,到最后一轮的时候,肯定是要打出伤害的,无论你当时的增幅有多猛,由于以后不能操作了,都无济于事,所以一定会发动攻击,所以很明显dp[n][1][n]=a[n]
由于100*100*5050炸空间了,所以需要用滚动数组,因为每一轮都只需要下一轮的情况,所以只用开个2,然后通过奇偶性不断翻转就行
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
const int MAXN = 1e5+5;
const int MOD = 1e9+7;
int t,n;
ll a[MAXN],b[MAXN],c[MAXN];
ll dp[2][105][5055];
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
memset(dp,0,sizeof(dp));
rep(i,1,n){
scanf("%I64d%I64d%I64d",&a[i],&b[i],&c[i]);
}
dp[n&1][1][n]=a[n];
per(i,n-1,1){
rep(j,1,n-i){
int down=(i+1+i+j)*j/2,up=(n-j+1+n)*j/2;
rep(k,down,up){
dp[i&1][j+1][k+i]=max(dp[i&1][j+1][k+i],dp[(i+1)&1][j][k]+a[i]);
dp[i&1][j][k]=max(dp[i&1][j][k],dp[(i+1)&1][j][k]+(k-i*j)*b[i]);
dp[i&1][j][k]=max(dp[i&1][j][k],dp[(i+1)&1][j][k]+j*c[i]);
}
}
}
ll ans=0;
rep(j,1,n)rep(k,n,(1+n)*n/2)ans=max(ans,dp[1][j][k]);
printf("%I64d\n",ans);
}
return 0;
}