AcWing 135. 最大子序和(单调队列优化DP)

AcWing 135. 最大子序和(单调队列优化DP)

一、问题

在这里插入图片描述

二、分析

涉及到一段区间和的问题,我们最容易想到的是前缀和算法。

因此,我们可以假设我们的最优解区间是 [ l , r ] [l,r] [l,r],同时将前缀和数组记作 s [ i ] s[i] s[i],那么我们的区间和就可以表示为 s [ r ] − s [ l − 1 ] s[r]-s[l-1] s[r]s[l1],假设我们固定一右端点 r r r,那么如果想要让这个差值最大,在 s [ r ] s[r] s[r]不变的情况下,只能是 s [ l − 1 ] s[l-1] s[l1]最小。

也就是说,我们需要在 r r r的右侧寻找一个最小值,但是这个最小值的所在范围是有一定约束的,因为题目中说的是区间长度不超过 m m m,所以我们需要在 r r r右侧 m + 1 m+1 m+1的范围内找一个最小值。

为什么是 m + 1 m+1 m+1?

因为我们想要找 [ l , r ] [l,r] [l,r]的区间和,用到的是 s [ l − 1 ] s[l-1] s[l1],也就是说如果区间长度是 m m m,我们减去的是左侧距离 m + 1 m+1 m+1的那个数。

而随着右端点的移动,这个长度为 m m m的范围也在向右移动,相当于一个滑动窗口。

而对于滑动窗口求最值的方法就是我们在之前介绍过的单调队列

如果读者对单调队列不熟悉的话,作者在算法专栏中写过一篇文章:第九章:单调栈与单调队列,这里面详细地介绍了该算法和一些关键点的讨论。

这里就直接用单调队列了。

因此,我们只需要枚举右端点,一边枚举右端点一边更新滑动窗口。

这道题其实和DP没有太大的关系。

只不过我们的右端点 r r r是从小到大枚举的,类似于DP从小到大求解问题的思想。

但状态和状态之间没有必然的联系。

三、代码

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int a[N], s[N];
int q[N], h, t;
void solve()
{
	int n, m;
	cin >> n >> m;
	for(int i = 1; i <= n; i ++ )
	{
		scanf("%d", a + i);
		a[i] += a[i - 1];
	}
	int res = -0x3f3f3f3f;
	for(int i = 1; i <= n; i ++ )
	{
		if(h <= t && q[h] < i - m )h++;
		res = max(res, a[i] - a[q[h]]);
		while(h <= t && a[q[t]] >= a[i])t--;
		q[ ++ t] = i;
	}
	cout << res << endl;
}

int main()
{
	solve();
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值