题目描述:
输入一个长度为 n𝑛 的整数序列,从中找出一段长度不超过 m 的连续子序列,使得子序列中所有数的和最大。
注意: 子序列的长度至少是 11。
输入格式
第一行输入两个整数 n,m𝑛,𝑚。
第二行输入 n𝑛 个数,代表长度为 n𝑛 的整数序列。
同一行数之间用空格隔开。
输出格式
输出一个整数,代表该序列的最大子序和。
数据范围
1≤n,m≤3000001≤𝑛,𝑚≤300000,
保证所有输入和最终结果都在 int 范围内。
输入样例:
6 4
1 -3 5 1 -2 3
输出样例:
7
我一看到这个题,首先想到的就是,用前缀和
来做,因为题目说了,长度不超过m的``连续子序列
但是长度不超过m
这个,我还真不知道该怎么写。最后,我一看这道题的标签是单调队列
。于是我知道该怎么做了。如果我们就单纯的使用前缀和
没有办法很好的找到最大值,因为我们的时间复杂度太高了 O(n^2)
,怎么优化呢?问题也就转变成了,求 m + 1区间中的最小值,但是,时间复杂度要求是O(n)
因此,使用单调队列就解决了这个问题,废话不多说,直接上代码
#include <iostream>
using namespace std;
const int N = 3e5 + 10;
int q[N], hh, tt;
int s[N], n, m;
void print(){
for(int i = hh; i <= tt; i ++){
cout << s[q[i]] << " ";
}
cout << endl;
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i ++){
cin >> s[i];
s[i] += s[i - 1];
//cout << s[i] << " ";
}
//cout << endl;
int res = 0x80000000;
//找出区间中最小的值
for(int i = 1; i <= n; i ++){
if(i - q[hh] > m) hh ++;
//为什么要把 res 的计算放到最上面呢?
//因为,如果我们把这条语句放到下面的话,我们就没有判断第一个元素,(就单纯的第一个元素也是一个子序列)
//如果我们先插入的第一个元素,那么s[i] 和 s[q[hh] 就是同一个值,这样就会导致 res = 0 就永远不会出现负数
res = max(res, s[i] - s[q[hh]]);
//队列不为空 && 队尾元素大于插入的元素,那么队尾元素出队(单调队列)
while(hh <= tt && s[q[tt]] >= s[i]) tt --;
q[++ tt] = i; // 入队列
//cout << "当前元素为 " << s[i] << endl;
//print();
}
cout << res << endl;
}
为什么初始化的时候,tt 没有等于 -1?
这是因为,我们默认情况下,是在队列中已经插入了一个元素的,因为我们求的是前缀和,插入了一个前缀和标杆0,不加0的话,如果第一个元素能取到的情况下,每次都取不到,模拟下就能明白,因为减的是s[l - 1]
因为求的是i之前,不包括i的长度为m的滑动窗口的最小值,在前m个元素为正值时,s[i]递增,这时窗口内最小值为s[0],表示取1~i个元素求和,所以s[0]应当为第一个元素。这也是为什么先判断队头是否出队,接着取max再将i元素入队的原因(正常是,先将i入队,再判断队头是否出队)。
为什么要先计算res在插入当前元素?
因为把s[i]加到队列里后可能会把之前的最小值弹出,自己变成最小值了,会丢掉之前m长度的最小值记录
都写到这里了,再把单调队列
的模版再整理一下叭!!
ACWing 154.滑动窗口
这是单调队列的模版题!!
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int a[N], n, m;
int q[N], hh, tt = -1;
int main(){
cin >> n >> m;
for(int i = 0; i < n; i ++){
cin >> a[i];
if(i - q[hh] + 1 > m) hh ++;
while(hh <= tt && a[q[tt]] >= a[i]) tt --;
q[++ tt] = i;
if(i < m - 1) continue;
cout << a[q[hh]] << " ";
}
cout << endl;
hh = 0, tt = -1;
for(int i = 0; i < n; i ++){
if(i - q[hh] + 1 > m) hh ++;
while(hh <= tt && a[q[tt]] <= a[i]) tt --;
q[++ tt] = i;
if(i < m - 1) continue;
cout << a[q[hh]] << " ";
}
cout << endl;
}