输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大。
例如 1,-3,5,1,-2,3
当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;
}

本文介绍了一种使用单调队列解决最大子序列和问题的方法,该问题是在长度为n的整数序列中寻找不超过M长度的连续子序列,使得其和最大。通过优化算法将时间复杂度降低至O(n),适用于大数据量的快速计算。
485

被折叠的 条评论
为什么被折叠?



