https://vjudge.net/contest/454412#problem/I
有攻击力A,和攻击增量D,每轮A会增加D,问最多能造成多少伤害。
有n个回合,每轮能进行3种操作:
1.
造
成
(
A
+
a
i
)
的
伤
害
1.造成(A+a_i)的伤害
1.造成(A+ai)的伤害
2.
D
增
加
b
i
2.D增加b_i
2.D增加bi
3.
A
增
加
c
i
3.A增加c_i
3.A增加ci
首先看到这该死的后效性就知道贪心不太行, 而且正向dp也不行,得逆向dp,并且因为第n轮一定是进行攻击操作,初始化也解决了,现在的问题就是状态和转移方程,我刚开始想的是dp[i][j],表示第i轮,从i-n进行了j次攻击,但是因为该死的D,如果不知道是哪些回合进行了攻击是无法计算的,然后我就卡在这了,最后看了大佬们的题解,只要再加一维表示进行了攻击的回合的下标和就好了,因为第i轮D增加
b
i
b_i
bi,对第j轮的贡献为
(
j
−
i
)
∗
b
i
(j-i)*b_i
(j−i)∗bi,则对i-n的总贡献为
∑
1
j
(
k
j
−
i
)
∗
b
i
=
(
s
u
m
−
j
∗
i
)
∗
b
i
\sum_1^j(k_j-i)*b_i=(sum-j*i)*b_i
∑1j(kj−i)∗bi=(sum−j∗i)∗bi,完美解决了上面的问题,然后又出现了新的问题,sum最大是5050,i、j是100,这样会mle,所以又要滚动i.
定义
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示i-n轮中有j次攻击操作,这些攻击操作回合的下标和为k时造成的最大伤害.初始化dp[n&1][1][n]=a[n].
最后转移方程如下
d
p
[
i
&
1
]
[
j
+
1
]
[
k
+
i
]
=
m
a
x
(
d
p
[
i
&
1
]
[
j
+
1
]
[
k
+
i
]
,
d
p
[
(
i
+
1
)
&
1
]
[
j
]
[
k
]
+
a
i
]
)
;
\ \ \ \ \ \ \ \ \ \ \ 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+1][k+i]=max(dp[i&1][j+1][k+i],dp[(i+1)&1][j][k]+ai]);
d
p
[
i
&
1
]
[
j
]
[
k
]
=
m
a
x
(
d
p
[
i
&
1
]
[
j
]
[
k
]
,
d
p
[
(
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]+(k-j*i)*b_i]);
dp[i&1][j][k]=max(dp[i&1][j][k],dp[(i+1)&1][j][k]+(k−j∗i)∗bi]);
d
p
[
i
&
1
]
[
j
]
[
k
]
=
m
a
x
(
d
p
[
i
&
1
]
[
j
]
[
k
]
,
d
p
[
(
i
+
1
)
&
1
]
[
j
]
[
k
]
+
j
∗
c
i
]
)
;
\ \ \ \ \ \ \ \ \ \ \ dp[i\& 1][j][k]=max(dp[i\&1][j][k],dp[(i+1)\&1][j][k]+j*c_i]);
dp[i&1][j][k]=max(dp[i&1][j][k],dp[(i+1)&1][j][k]+j∗ci]);
#include <iostream>
#include <cstring>
#include <cmath>
#include <bitset>
#include <queue>
#include <vector>
#include <cstdio>
#include <set>
#include <stack>
#include <assert.h>
#include <sstream>
#include <cstring>
#include <algorithm>
#include <map>
#define rep(i, a, b) for (long long i= a; i <= b; i++)
#define reps(i, a, b) for (long long i = a; i >= b; i--)
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a*b/gcd(a,b)
#define ls(node) tree[node].ls
#define rs(node) tree[node].rs
#define l(node) tree[node].l
#define r(node) tree[node].r
#define val(node) tree[node].val
#define sum(node) tree[node].sum
#define lazy(node) tree[node].lazy
#define lazy1(node) tree[node].lazy1
#define lazy2(node) tree[node].lazy2
#define tr(node) tree[node]
//#pragma GCC optimize(2)
using namespace std;
const int N=1e2+7;
const int M=3e4+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
typedef long long ll;
#define Rep(i,a,b)for(ll i=a;i<=b;i++)
inline ll read()
{
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
ll a[N],b[N],c[N],A[N],dp[2][N][N*(N+1)/2];
int main()
{
ll t;
t=read();
while (t--){
ll n=read(),ans=0;
memset(dp,0,sizeof dp);
rep(i,1,n){
a[i]=read(),b[i]=read(),c[i]=read();
}
dp[n&1][1][n]=a[n];//第n轮必定进行攻击
reps(i,n-1,1){
rep(j,1,n-i){
int x=n+(i+i+j)*(j-1)/2,y=(n+n-j+1)*j/2;//下标和上下限,显然最左边的j-1个加n
//是下限,最右边的j个是上限
rep(k,x,y){
dp[i&1][j+1][k+i] = max(dp[i&1][j+1][k+i],dp[(i+1)&1][j][k]+a[i]);//第i轮攻击
dp[i&1][j][k] = max(dp[i&1][j][k],dp[(i+1)&1][j][k]+(k-j*i)*b[i]);//让D增加b[i]
dp[i&1][j][k] = max(dp[i&1][j][k],dp[(i+1)&1][j][k]+j*c[i]);//让A增加c[i]
}
}
}
rep(i,1,n){
rep(j,1,n*(n+1)/2){
ans=max(ans,dp[1][i][j]);
}
}
printf("%lld\n",ans);
}
}