Misunderstood … Missing(DP)

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.Dbi
3. A 增 加 c i 3.A增加c_i 3.Aci

首先看到这该死的后效性就知道贪心不太行, 而且正向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 (ji)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(kji)bi=(sumji)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]+(kji)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]+jci]);

#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);
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值