一本通#10176「一本通 5.5 例 2」最大连续和题解
题面:
题目描述
给你一个长度为 n 的整数序列,要求从中找出一段连续的长度不超过 m 的非空子序列,使得这个序列的和最大。
输入格式
第一行为两个整数 n,m;
第二行为 n 个用空格分开的整数序列,每个数的绝对值都小于 1000。
输出格式
仅一个整数,表示连续长度不超过 m 的最大非空子序列和。
样例
输入
6 4
1 -3 5 1 -2 3
输出
7
数据范围与提示
对于 \(50\%\) 的数据\(1\leq n\leq 10^4\)
对于 \(100\%\) 的数据,\(1\leq n\leq 10^5\)
题解
题目范围为\(1\)<=n<=\(10^5\)所以要O(N)的代码
我们从非空子序列和可以看出要有前缀和
设区间为[l,r]则区间和为sum[r]-sum[l-1]
我们把l固定下来则就要是sum[l-1]最小并且l-1要与r的距离不超过m(我代码中k是m)
那就会让人回想起优先队列滑动窗口求窗口内的最小值
我们这里把窗口大小设为m,求i(即区间的l)前使得sum[l-1]最小
然后就可以写代码了
样例代码有注释
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
int n,k,a[N],sum[N],dp[N],ans=INT_MIN;
deque<int> q;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
for(int i=1;i<=n;i++){
while(!q.empty()&&q.front()<i-k) q.pop_front();//<i-k是特殊处理正常单调队列是<=i-k
//这里特殊处理是因为19行 q.push_back(i-1); 因为我们要算前缀和所以-1但是我们只要区间
//左端不与右端差超过k就行而 左端=q.front()+1,右端=i 所以条件是q.front()+1<=i-k即
//q.front()<i-k
while(!q.empty()&&sum[q.back()]>=sum[i-1]) q.pop_back();
q.push_back(i-1);//19行特殊处理原因在上面
if(!q.empty()) ans=max(ans,sum[i]-sum[q.front()]);
}
cout<<ans;
return 0;
}