一个合法的序列Bessie可以选择的是0[0], 1[0], 3[2], 2[1], 0[0]。(括号里的数表示钱数) 这样,可以得到的钱数为0+0+2+1+0 = 3。 如果Bessie选择一个序列开头为0, 1, 2, 3, ...,那么她就没办法跳回去了,因为她没办法再跳到一个之前没跳过的格子。序列0[0], 2[1], 4[-3], 5[4], 3[2], 1[0], 0[0]是最大化钱数的序列之一,最后的钱数为(0+1-3+4+2+0 = 4)。BZOJ_1915_[Usaco2010 Open]奶牛的跳格子游戏_DP+单调队列
一个合法的序列Bessie可以选择的是0[0], 1[0], 3[2], 2[1], 0[0]。(括号里的数表示钱数) 这样,可以得到的钱数为0+0+2+1+0 = 3。 如果Bessie选择一个序列开头为0, 1, 2, 3, ...,那么她就没办法跳回去了,因为她没办法再跳到一个之前没跳过的格子。序列0[0], 2[1], 4[-3], 5[4], 3[2], 1[0], 0[0]是最大化钱数的序列之一,最后的钱数为(0+1-3+4+2+0 = 4)。
4
OUTPUT DETAILS:
还有一种可能的最大化钱数的序列是: 0 2 4 5 3 1 0
设F[i]表示走到第i个点,给第i-1留下位置的最大收益。
i能走到i+K,这部分如果走一定只走正数。
于是DP转移:$F[i]=F[j]+sum[i-2]-sum[j]+V[i]+V[i-1](i-K\le j\ge i-2)$。其中sum为只选正数的前缀和。
单调队列优化一下即可。
因为最远可以从i+K-1跳到i-1,最后的答案还要加上这部分。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define N 250050
int n,a[N],Q[N],K,l,r;
ll s[N],f[N],ans;
int main() {
scanf("%d%d",&n,&K);
int i;
for(i=1;i<=n;i++) scanf("%d",&a[i]),s[i]=s[i-1]+max(a[i],0);
if(K==1) {printf("%d\n",max(0,a[1])); return 0;}
f[0]=0; f[1]=a[1];
r++;
for(i=2;i<=n;i++) {
while(l<r&&Q[l]<i-K) l++;
int j=Q[l];
f[i]=f[j]+s[i-2]-s[j]+a[i]+a[i-1];
while(l<r&&f[Q[r-1]]-s[Q[r-1]]<=f[i-1]-s[i-1]) r--;
Q[r++]=i-1;
}
for(i=1;i<=n;i++) {
ans=max(ans,f[i]+s[min(n,i+K-1)]-s[i]);
}
printf("%lld\n",max(ans,s[K]));
}
BZOJ_1915_[Usaco2010 Open]奶牛的跳格子游戏_DP+单调队列
原文:https://www.cnblogs.com/suika/p/9215431.html