BZOJ2442 [Usaco2011 Open]修剪草坪
Description
给定一个n(n<=1000000)个数的正整数序列,要求选取的数连续不超过k,问最大选取值为多少。
题解
因为序列是正整数,所以我们每次空着不选的长度一定是1。
于是我们可以很容易地推出状态转移方程。
钦定f[i]为序列到i,且第i位不选的最大答案,用sum[i]表示前缀和
我们有
f[i]=max(f[i],f[j]+sum[i−1]−sum[j]) (max(i−k−1,0)<=j<k)
可是这样的复杂度是 O(n2) ,会超时。
所以我们得想办法优化。
观察状态转移方程,我们发现选取的数是一个长度为k的区间的f[i]-sum[j]的最大值再加上sum[i-1]。所以可以用单调队列来优化,这样的复杂度就减小到了O(n)。
#include <cstdio>
#include <cmath>
#include <queue>
#include <algorithm>
#include <cstring>
#define MAXN 100000+10
#define LL long long
using namespace std;
int k,n,t,h;
LL f[MAXN],a[MAXN],sum[MAXN],ans;
struct Date{
LL n,c;
}q[MAXN];
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum[i]+=sum[i-1]+a[i];
for(int i=1;i<=n+1;i++)
{
while(q[h].n<i-k-1) h++;
f[i]=max(f[i],q[h].c+sum[i-1]);
while(f[i]-sum[i]>=q[t].c&&t>=h) q[t].c=q[t].n=0,t--;
q[++t].c=f[i]-sum[i];q[t].n=i;
ans=max(f[i],ans);
}
printf("%lld\n",ans);
return 0;
}