题目链接:传送门
描述
输入一个长度为n的整数序列,从中找出一段不超过m的连续子序列,使得整个序列的和最大。
例如 $1,-3,5,1,-2,3$。
当 $m=4$ 时,$S=5+1-2+3=7$;
当 $m=2$ 或 $m=3$ 时,$S=5+1=6$。
输入格式
第一行两个数 $n,m(n,m le 300000)$
第二行有 $n$ 个数,要求在 $n$ 个数找到最大子序和
输出格式
一个数,数出他们的最大子序和
样例输入
6 4
1 -3 5 1 -2 3
样例输出
7
题解:
设这 $n$ 个数的前缀和为 $s_1 sim s_n$。考虑枚举右端点 $i$,当右端点 $i$ 固定时,显然连续子序列的和为 $s_i - s_{j-1}$。
因此相当于我们要寻找一个点 $y$,使其满足 $y in [i-m, i-1]$ 的前提下 $s_y$ 最小。
显然,当对于满足 $i-m le x < y < i$ 的两个点 $x,y$,如果 $s_x ge s_y$,显然 $x$ 点不能成为最优解,因此可以直接舍弃掉 $x$。
因此,用一个双端队列维护 $j$,队列中的 $j$ 满足随着下标的递增,相应的 $s_j$ 也是递增的。
这样一来,每次对于 $i$ 寻找 $j$,只要从头部抛去 $s_$ 点,剩下来的队头就是最优的 $j$。
而当前 $i$ 入队,为了保持队列的递增性,因此要在队尾抛去所有 $s_{back} ge s_i$ 的点。
时间复杂度:所有点出入队一次,$O(n)$。
AC代码:
#include<bits/stdc++.h> using namespace std; const int INF=0x3f3f3f3f; const int maxn=300000+10; int n,m,ans; int a[maxn],s[maxn]; deque<int> Q; int main() { std::ios::sync_with_stdio(false); cin>>n>>m; ans=-INF, s[0]=0; Q.push_back(0); for(int i=1;i<=n;i++) { cin>>a[i], s[i]=s[i-1]+a[i]; while(!Q.empty() && Q.front()<i-m) Q.pop_front(); ans=max(ans,s[i]-s[Q.front()]); while(!Q.empty() && s[Q.back()]>=s[i]) Q.pop_back(); Q.push_back(i); } cout<<ans<<endl; }