chen03 正在打 爆零球 保龄球。
在 chen03 的面前有n个球瓶等距排成一排,从左到右的编号分别为 1,2,...,n。每个球瓶都有一个分值(可能为负数),第i个球瓶的分值为ai。每当 chen03 用球击倒一个球瓶,他就会得到相应的分值。
chen03 有 k 个保龄球,每个球的直径为w。也就是说,每个球可以击倒一个长度为w的区间内的所有球瓶。当然,每个球只能投出一次。
在某个球瓶被击倒后,球瓶原来的位置会留出空位。另外,球瓶1的左边、球瓶n的右边都有足够的空位。chen03 投出的保龄球可以经过空位,空位上原有的球瓶失效,即 chen03 不会得到相应的分值。当然,保龄球可以不击倒任何球瓶。
chen03 想知道,投完所有保龄球后,他最多可以得到多少分。
输入
共两行。第一行3个正整数n,k,w,分别表示球瓶数量、球的数量、球的直径。
第二行n个整数a1,a2,...,an,表示每个球瓶的分值。
输出
一行一个整数,表示 chen03 能得到的最大的分值。
样例输入 Copy
【样例1】
9 2 3
2 8 5 1 9 6 9 3 2
【样例2】
9 3 3
2 8 -5 3 5 8 4 8 -6
样例输出 Copy
【样例1】
39
【样例2】
38
提示
样例1解释:第一次击打区间[1,3]内的球瓶,第二次击打区间[5,7]内的球瓶。得分为 (2+8+5)+(9+6+9)=39。
样例2解释:第一次击打区间[1,2]内的球瓶(用了球瓶1左边的空位),第二次击打区间[4,6]内的球瓶,第三次击打区间 [7,8]内的球瓶(用了球瓶6留下的空位)。得分为(2+8)+(3+5+8)+(4+8=38 。
中文题意
首先考虑dp状态前i个用了k次保龄球
设f数组为前缀和
但是会发现第二个样例过不了,因为可以打空位(这种骚操作真的是可以的)
然后就在此基础上仔细分析一下这个情况:
假设当前位置为pos,什么时候可以应用空位呢?
假设我用了 pos-x的空位,那么pos-x一定是被击打完了,此时我们只需要f[pos] - f[pos-x] + dp[pos][k-1]
然后分析一下 x可取范围在 0<=x<=w
由以上状态可知,现在我们需要从i位置向前是否击打完了(因为击打完了,才有可能出现空位操作)
所以我们设三维的dp状态dp[i][k][0/1]表示在第i个位置,用了k个保龄球,第i个向前是否击打过
那么状态转移就来了,由上述可知我们可以枚举x
注意此时别忘了
如果x取w的话,还是可以由dp[i-w][k-1][0]转移过来的 —— 记得不要忘记这个转移
之后的话 就有一个n*m*w的写法,但是可能卡不过去
所以加了一下单调队列优化
ll n,m,p;
ll dp[10205][505][2];
ll f[maxn],a[maxn];
ll q[maxn];
int main()
{
read(n);read(m);read(p);
for(int i=1;i<=n;i++){
read(a[i]);
f[i] = f[i-1]+a[i];
}
for(int i=n+1;i<=n+p-1;i++) f[i] = f[n];
ll maxl = 0;
for(int i=1;i<=n+p-1;i++){
for(int k=1;k<=m;k++){
dp[i][k][0] = dp[i][k][1] = -INF;
}
}
for(int i=1;i<=n+p-1;i++) dp[i][0][1] = -INF;
for(int k=1;k<=m;k++){
int r = 0,l = 1;
q[r = 1] = 0;
for(int i=1;i<=n+p-1;i++){
dp[i][k][0] = max(dp[i][k][0],max(dp[i-1][k][0],dp[i-1][k][1]));
while(l<=r&&dp[q[r]][k-1][1]-f[q[r]]p) l++;
dp[i][k][1] = max(dp[i][k][1],dp[q[l]][k-1][1]-f[q[l]]+f[i]);
int x = max(i-p,0ll);
dp[i][k][1] = max(dp[i][k][1],dp[x][k-1][0]+f[i]-f[x]);
maxl = max(maxl,max(dp[i][k][0],dp[i][k][1]));
}
}
printf("%lld\n",maxl);
return 0;
}