【单调队列】最大子序和

在这里插入图片描述
初步学习单调队列,
先从简单点的开始理解。

每次找出在(i-m,i)范围内的最小sum[j]值。

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
ll sum[maxn];
list<int> lt;
int main(){
	int n,m;
	while(~scanf("%d%d",&n,&m)){
		lt.clear();
		sum[0]=0;
		for(int i=1;i<=n;i++){
			scanf("%lld",&sum[i]);
			sum[i]=sum[i-1]+sum[i];
		}
		ll maxs=sum[1];
		lt.push_front(1);
		for(int i=2;i<=n;i++){
			while(!lt.empty()&&i-lt.back()>m){
				lt.pop_back();
			}
			maxs=max(maxs,sum[i]-sum[lt.back()]);
			while(!lt.empty()&&sum[i]<sum[lt.front()]){
				lt.pop_front();
			}
			lt.push_front(i);
		}
		printf("%lld\n",maxs);
	}
	return 0;
}

初始化先用sum数组存储前缀和,
sum[i]-sum[j]这样就可以求出区间和了。
然后在第二个for循环里更新

因为题目要求是在m的范围内
所以每次进来先检测是否 i - 队列的末尾>m,

while(!lt.empty()&&i-lt.back()>m){
	lt.pop_back();
}

(为什么不是等于,因为前缀和数组的被减数是包含前面的)

之后更新最大值maxs。

maxs=max(maxs,sum[i]-sum[lt.back()]);

为了保证队列是从大到小,并且每次要求sum[j]尽可能小,所以,

while(!lt.empty()&&sum[i]<sum[lt.front()]){
	lt.pop_front();
}

如果队首的元素的sum比当前的sum[ j ] 大,那就pop掉,
因为当前的 i 又新它的sum又小 ,

最后

lt.push_front(i);

将i插入队首就好

发布了62 篇原创文章 · 获赞 0 · 访问量 653

猜你喜欢

转载自blog.csdn.net/weixin_44745441/article/details/105006710