浅谈单调队列

单调队列,顾名思义就是一个元素之间的关系具有单调性的队列

我们通过一道例题来讲解

最大子序和

给定一个长度为 N N N 的整数序列(可能有负数),从中找出一段长度不超过 M M M 的连续子序列,使得子序列中所有数的和最大。 $N,M \le 3 \cdot 10^5 $。

计算“区间和”的问题,一般转化为“两个前缀和相减”的形式进行求解。我们先求出 S [ i ] S[i] S[i] 表示序列里前 i i i 项的和,则连续子序列 [ L , R ] [L,R] [LR] 中的数的和就等于 S [ R ] − S [ L − 1 ] S[R] - S[L-1] S[R]S[L1]。那么原问题就可以转化为:

找出两个位置 x , y x,y x,y 使 S [ y ] − S [ x ] S[y] - S[x] S[y]S[x] 最大并且 y − x ≤ M y - x \le M yxM

首先我们枚举右端点 i i i,当 i i i 固定时,问题就变为:

找到一个左端点 j j j,其中 j ∈ [ i − m , i − 1 ] j \in [i-m,i-1] j[im,i1] 并且 S [ j ] S[j] S[j] 最小。

不妨比较一下任意两个位置 j j j k k k

如果 k < j < i k<j<i k<j<i 并且 S [ k ] ≤ S [ j ] S[k] \le S[j] S[k]S[j]

那么对于所有大于等于 i i i 的右端点,永远不会成最优选择。

这是因为不但 S [ k ] ≮ S [ j ] S[k] \not < S[j] S[k]<S[j],而且 j j j i i i 更近,长度更不容易超过 M M M,即 j j j 的生存能力比 k k k 更强。所以当 j j j 出现后, k k k 就完全是一个无用的位置。

以上比较告诉我们,可能成为最优选择的策集合一定是一个

“下标位置递增、对应的前缀和S的值也递增” 的序列。

我们可以用一个队列保存这个序列。随着右端点位置改变,从前向后扫描,我们对每个 i i i 执行以下三个步骤:

  1. 判断队头决策与 i i i 的距离是否超出 M M M 的范围,若超出则出队。
  2. 此时队头就是右端点为 i i i 时,左端点 j j j 的最优选择。
  3. 不断删除队尾决策,直到队尾对应的S值小于 S [ i ] S[i] S[i]。然后把 i i i 作为一个新的决策入队。

实现方法:STL双端队列

deque<int> q;
while(q.size()) q.pop_front();
q.push_back(0);
for(int i=1;i<=n;i++){
	while(q.size()&&i-q.front()>k) q.pop_front();
	ans=max(ans,sum[i]-sum[q.front()]);
	while(q.size()&&sum[q.front()]>=sum[i]) q.pop_back();
	q.push_back(i);
}

这就是著名的单调队列算法,因为每个元素至多入队一次、出队一次,所以整个算法的时间复杂度为 O ( N ) O(N) O(N)

它的思想也是 在决策集合(队列)中及时排除一定不是最优解的选择。 单调队列也是优化动态规划的一个重要手段。

coding

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ycw-123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值