题目:https://ac.nowcoder.com/acm/contest/366/I
题意:初始有一个攻击力A=0,,攻击力增量D=0,,给你n个回合,
在每轮回合初,A会自动加上D,每轮回合有三种操作可以选择
1、直接用A+a[i]攻击产生伤害,伤害为A+a[i]
2、增加成长,D+=b[i]
3、增加攻击力,A+=c[i]
要求n轮回合后产生的最大伤害是多少?
思路:
乍一看感觉好复杂,但仔细分析可知第n轮回合肯定是攻击,既然知道最后一次的,那么我们可以往前推
用dp[i][j][k],i表示第i个回合,j表示从i+1到n轮回合中发生了几次攻击,k表示这j次攻击的编号和,
dp[1:100][1:100][1:100*101/2],这样显然空间不够,所以要用滚动数组
时间复杂度100*100*100*101/2
若选1可得dp[i&1][j+1][k+i]=max(dp[i&1][j+1][k+i],dp[(i+1)&1][j][k]+a[i])
若选2可得dp[i&1][j][k]=max(dp[i&1][j][k],dp[(i+1)&1][j][k]+(k-j*i)*b[i]);
其中(k-j*i)*b[i]表示从i+1到n回合中的每次攻击对第i回合选择2多产生的伤害
若选3可得dp[i&1][j][k]=max(dp[i&1][j][k],dp[(i+1)&1][j][k]+j*c[i])
初始状态:dp[n&1][1][n]=a[n]
AC code:
#include<bits/stdc++.h>
using namespace std;
const int maxn=102;
typedef long long ll;
ll dp[2][maxn][maxn*maxn/2];
ll a[maxn],b[maxn],c[maxn];
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof dp);
scanf("%d",&n);
for(int i=1; i<=n; i++) scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
dp[n&1][1][n]=a[n];
for(int i=n-1; i>=1; --i)///n-1开始选,n必选,第n步必攻击
for(int j=1; j<=n-i; j++)
{
int l=(j-1)*(2*i+j)/2+n;
int r=j*(2*n-j+1)/2;
for(int k=l; k<=r; k++)
{
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-j*i)*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;
for(int i=1; i<=n; i++)
for(int k=1; k<=5050; k++)
ans=max(ans,dp[1][i][k]);
printf("%lld\n",ans);
}
return 0;
}