传送门:https://codeforces.com/gym/102056/problem/I
题意
刚开始,有初始值都为0的A(伤害)和D(伤害增量)。 D的含义是,每回合刚开始都可以给A增加D的伤害。 有n个回合,每个回合有3个值a,b,c。
然后三个操作:
- 操作一:可以攻击,总伤害增加A+a。
- 操作二:给增量D增加b。
- 操作三:给伤害A增加c。
问如何操作可以使总伤害值最大。n<=100。
思路
首先可以想到$3^{100}$复杂度的暴力,显然不行。
然后正向遍历?显然也不行,因为你前面的操作对后续会有影响,所以考虑逆向。
那该怎么逆向呢?如果我们知道我们攻击了几次,并且哪几次攻击,就可以算出当前的贡献。
比如n=5,我正在第2天,如果我们第3和第5天攻击了,那么当前有三个操作:
- 执行操作一:$ans+=A+a[i]$
- 执行操作二:$ans+=(3-2)*b[i]+(5-2)*b[i]$
- 执行操作三:$ans+=2*b[i]$
很显然,这些我们可以O(1)判断,所以我们要根据后面的选择来做出前面的选择,这就是逆向dp。
那我们需要知道哪些状态呢?第几轮,攻击次数(统计c[i]贡献),哪几次攻击(统计b[i]贡献)。
第几轮?最大为100,i。 攻击次数?最大为100,j。 哪几次攻击?这个很不容易想到,那就是记录攻击次数下标和,k。
那么就得到dp[i][j][k]表示第i轮,攻击了j次,并且下标和为k的最大伤害。
那么就可以进行状态转移了:
- $操作一:dp[i][j+1][k+i]=max(dp[i][j+1][k+i], dp[i+1][j][k]+a[i])$
- $操作二:dp[i][j][k]=max(dp[i][j][k],dp[i+1][j][k]+(k-j*i)*b[i])$
- $操作三:dp[i][j][k]=max(dp[i][j][k],dp[i+1][j][k]+j*c[i])$
所以先逆序遍历n,在遍历j,最后遍历k。
想到这里就已经很好了,可能是我们没有精力继续想了,没有优化。所以一直过不去。
我们知道n有100,所以100*100*(1+100)*100/2会MLE,然后看到i其实可以滚动的。
所以把前面都改成$i\%2$,后面都改成$(i+1)\%2$,减小空间复杂度。
k其实也能优化,不必要1到5050,可以根据i和j得到上下限。 上限:从i开始的j-1次攻击加上最后一次为$(i+i+j-1)*(j-1)/2+n$ 下限:从n-j+1开始的j次攻击为$(n-j+1+n)*j/2$
最后$ans=max(ans,dp[1][j][k])$处理答案即可。
这一题其实可以写的,可以说90%都想到了,但是ACM就是这样,不允许一点错误,AC即是王道。
Code
1 | # |
本文作者:jujimeizuo
本文地址: https://blog.jujimeizuo.cn/2021/04/07/2018-ec-finals-i-misunderstood-missing/
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0 协议。转载请注明出处!