【upc】 保龄球 (bowling) | 单调队列优化dp

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数组为前缀和

dp[i][k] = max(dp[i-1][k],dp[i-w][k-1]+f[i] - f[i-w])

但是会发现第二个样例过不了,因为可以打空位(这种骚操作真的是可以的)

然后就在此基础上仔细分析一下这个情况:

假设当前位置为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

dp[i][k][0] = max(dp[i-1][k][0],dp[i-1][k][1]);

dp[i][k][1] = min(\{dp[i-x][k-1][1]+f[i]-f[i-x]\}) | 0<=x<= w

注意此时别忘了

如果x取w的话,还是可以由dp[i-w][k-1][0]转移过来的 —— 记得不要忘记这个转移

之后的话 就有一个n*m*w的写法,但是可能卡不过去

所以加了一下单调队列优化

Code:

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]]<dp[i][k-1][1]-f[i]) r--;
            q[++r] = i;
            while(l<=r&&q[r]-q[l]>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;
}

猜你喜欢

转载自blog.csdn.net/qq_43857314/article/details/107717189
UPC