子序列的和 | ||||||
|
||||||
Description | ||||||
输入一个长度为n的整数序列(A1,A2,……,An),从中找出一段连续的长度不超过m的子序列,使得这个子序列的和最大。 |
||||||
Input | ||||||
有多组测试数据,不超过20组测试数据。
对于每组测试的第一行,包含两个整数n和m(n,m<=10^5),表示有n个数,子序列长度限制为m,表示这个序列的长度,第二行为n个数,每个数的范围为[-1000, 1000]。
|
||||||
Output | ||||||
对于每组测试数据,输出最大的子序列和,并换行。 |
||||||
Sample Input | ||||||
3 1
1 2 3
3 2
-1000 1000 1
|
||||||
Sample Output | ||||||
3
1001
|
||||||
Author | ||||||
黄李龙@HRBUST |
题目大意:求长度不超过m的最大子序列和
思路:做一个前缀和,每次查找离当前距离为M范围内的最小前缀和(可用RMQ)。
坑点:最小区间长度不能为0(查询的右区间只能为i-1而不能为i),RMQ查询其实坐标是1,但是1又是a[1]的值,所以RMQ维护的值整体向右平移1位。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int c[100005][20];
int a[100005];
void RMQ(int n)
{
int k=log((double)n)/log(2.0);
for(int i=1;i<=n;i++)
{
c[i][0]=a[i];
}
for(int j=1;j<=k;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
c[i][j]=min(c[i][j-1],c[i+(1<<(j-1))][j-1]);
}
}
}
int query(int l,int r)
{
int kk=r-l+1;
int k=log((double)kk)/log(2.0);
return min(c[l][k],c[r-(1<<k)+1][k]);
}
int main()
{
int n,m;
while(cin>>n>>m)
{
memset(c,0,sizeof(c));
int t;
a[0]=0;
a[1]=0;
for(int i=2;i<=n+1;i++)
{
scanf("%d",&t);
a[i]=t+a[i-1];
}
RMQ(n+1);
int ans=-0x3f3f3f3f;
for(int i=2;i<=n+1;i++)
{
ans=max(ans,a[i]-query(max(1,i-m),i-1));
//cout<<i<<" "<<m<<" "<<query(max(1,i-m),i-1)<<endl;
}
cout<<ans<<endl;
}
}