题面:链接:https://ac.nowcoder.com/acm/contest/366/I
题意:给定A和D初始值为0,一共n次操作,每次操作前会自动让A加上D的值,操作共有三种选择:
1.输出A+a[i]
2.D加上b[i]
3.A加上c[i]
求n次操作后输出和的最大值
思路:一开始想着正向DP,但发现无法判断2和3两个操作的优先级,然后发现可以不用知道A和D的值,从后往前dp,记录答案值的变化,如果作1操作,直接加上a[i],2操作就令答案值加上 dis(所有输出步数到该步的距离和)*b[i],3操作使答案值加上 j(输出步数)*c[i],1和3两个操作是比较好维护的,我在维护2操作的时候一开始直接用dis存目前所有输出步数到当前步的距离和,一直wa.......直接自闭,后来我发现直接存的话同一个距离值有可能会是某一步重复输出操作产生的,然后我就换成用dis记录所有输出步数到第一步的距离和,也就是所有输出步数的序号值和,然后2操作的时候减去当前步贡献的序号距离,再乘上b[i]即可
代码:
#include <bits/stdc++.h>
using
namespace
std;
typedef
long
long
ll;
const
ll mod = 1e9+7;
const
int
maxn = 120;
ll dp[2][maxn][5500];
int
t, n;
ll a[maxn], b[maxn], c[maxn];
int
main()
{
scanf
(
"%d"
,&t);
while
(t--)
{
scanf
(
"%d"
,&n);
for
(
int
i=1;i<=n;i++)
scanf
(
"%lld%lld%lld"
,&a[i],&b[i],&c[i]);
memset
(dp,0,
sizeof
(dp));
if
(n==1)
{
printf
(
"%lld\n"
,a[1]);
continue
;
}
dp[n%2][1][n] = a[n];
for
(
int
i = n-1; i>0 ;i--)
{
for
(
int
j = 1;j<=n-i;j++)
{
for
(
int
dis = (2*i+j)*(j-1)/2+n;dis<=(2*n-j+1)*j/2;dis++)
{
dp[i%2][j+1][dis+i] = max(dp[i%2][j+1][dis+i],dp[(i+1)%2][j][dis]+a[i]);
//1
dp[i%2][j][dis] = max(dp[i%2][j][dis],dp[(i+1)%2][j][dis]+1ll*c[i]*j);
//3
dp[i%2][j][dis] = max(dp[i%2][j][dis],dp[(i+1)%2][j][dis]+1ll*(dis-i*j)*b[i]);
//2
}
}
}
ll ans = 0;
for
(
int
i=1;i<=n;i++)
for
(
int
j = 1;j<=5050;j++)
ans = max(ans,dp[1][i][j]);
printf
(
"%lld\n"
,ans);
}
}