您的位置:首页 > 其它

最大子序和(单调队列优化)

2015-01-16 18:07 218 查看
输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大。

例如 1,-3,5,1,-2,3

当m=4时,S=5+1-2+3=7

当m=2或m=3时,S=5+1=6

input

第一行:n,m

第二行:n个数

output

最大的和sum

题目分析:首先我们知道O(n)可以很简单的解决原题(不限制子序列的长度)。但是这题加了限制(长度不超过m),并且数据为300000,并不能用O(n*m)的暴力计算最大前缀和之差的方法。所以要用到一些特殊的姿势~~~单调队列。

首先我们分析知道 f[i]=max(sum[i]-s[i-k]),k∈[1,m]。但是一般的维护只能是O(n*m)。变成f[i]=sum[i]-min(sum[i-k]),k∈[1,m]。我们只需要维护一个宽度为m的范围内的sum值的队列,然后每次用最小的去更新f[i],再将sum[i]也加入队列。用优先队列的思想,每个sum最多只能进一次队列出一次队列,所以复杂度是O(2*n)。

#include<iostream>
#include <bits/stdc++.h>
using namespace std;
int a[300100];
long long s[300100],f[300100],x[300100],w[300100];
int main()
{
int i,n,m,l,r;
long long ans=0;
scanf("%d%d", &n, &m);
memset(f,0,sizeof(f));
memset(s,0,sizeof(s));
for (i=1;i<=n;i++) {
scanf("%d", &a[i]);s[i]=s[i-1]+a[i];
}
l=1;r=1;x[1]=0;w[1]=0;
for (i=1;i<=n;i++) {
while (w[l]<i-m) l++;
f[i]=s[i]-s[w[l]];
while (s[i]<x[r]&&r>=l) r--;
r++;
x[r]=s[i];w[r]=i;
}
for (i=1;i<=n;i++)
if (f[i]>ans) ans=f[i];
printf("%I64d\n", ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: