单调队列 + 前缀和:连续子序列的最大和

梦想不抛弃苦心追求的人,只要不停止追求,你们会沐浴在梦想的光辉之中。再美好的梦想与目标,再完美的计划和方案,如果不能尽快在行动中落实,最终只能是纸上谈兵,空想一番。只要瞄准了大方向,坚持不懈地做下去,才能够扫除挡在梦想前面的障碍,实现美好的人生蓝图。单调队列 + 前缀和:连续子序列的最大和,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

单调队列 + 前缀和:连续子序列的最大和

单调队列

  单调队列能在O(N)的时间内方便地维护一个长度为n的序列中,每个长度为m的区间的区间最值。单调队列是一个双端队列,队首和队尾都可以进行出队操作,只有队尾可以进行入队操作。单调递增队列内部元素始终为单调递增。同理单调递减队列内部始终为单调递减。而我们对单调队列进行的所有操作就是为了维护上述单调性质。
  我们通过一个例子理解单调队列的运作方式:
在这里插入图片描述
  队列为单调递增队列时,若当前元素大于队尾元素,则当前元素入队,队列保持单调递增。若当前元素小于队尾元素,则我们需要将队尾元素不断出队,直到新的队尾元素小于当前元素,再将当前元素入队,这样才能保证队列单调递增。
  若当前遍历的是第i位元素,我们需要维护长度为m的子区间的最值,即队列中的元素必须在[i – m + 1,i]范围内,队首元素一定是队列中最早入队的元素,若队首元素不在此范围内,则将队首元素出队。
  我们通过队尾的出入队来维持队列的单调性质,用队首的出队来维持队列中元素都处在某个子区间内,由于队列的单调性,队首元素一定是该子区间内的最小或最大值

问题:

在这里插入图片描述
在这里插入图片描述

思路:

  题目要我们在一个序列A中找到长度不超过m子区间,使这个子区间的和最大。对于求子区间的和的问题,我们可以使用前缀和数组pre[][i,j]子区间的和即为pre[j] - pre[i - 1]。若我们确定子区间的终点j,长度不超过m的子区间即为[j - m + 1,j] ~ [j,j]。而这些子区间中的最大和为多少呢,通过子区间和的公式可以看出,这些子区间中的最大和为pre[j] - min(pre[j - m] ~ pre[j]),则我们需要知道pre[j - m] ~ pre[j]中的最小值,此时单调队列就登场了,即我们需要维护一个保存长度为m + 1的区间内最小值的单调队列。
  根据上述方法,我们可以求得确定右端点的子区间和的最大值,枚举每个元素为子区间的右端点,不断更新最大值即可
在这里插入图片描述

需注意的点
  java中ArrayDeque和LinkedList类实现了双端队列Deque接口,分别提供了双端队列的数组和链表实现。Deque接口的几个常用的方法如下:

方法 说明
void addFirst(E element) 将元素添加到队首
void addLast(E element) 将元素添加到队尾
E pollFirst() 队列不为空,删除并返回队头元素
E pollLast() 队列不为空,删除并返回队尾元素
E getFirst() 队列不为空,返回队头元素
E getLast() 队列不为空,返回队尾元素

代码:

import java.util.*;

public class Main {
    static ArrayDeque<Integer> q = new ArrayDeque<>();
    static long[] pre = new long[300002];
    public static void main(String[] args) {
        int n, m, i;
        long ans = 0, num;
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        m = scanner.nextInt();
        for (i = 1;i <= n;i++) {
            num = scanner.nextLong();
            pre[i] = pre[i - 1] + num;
            while (!q.isEmpty() && pre[q.getLast()] > pre[i]) {
                q.pollLast();
            }
            q.addLast(i);
            if (q.getFirst() < i - m) {
                q.pollFirst();
            }
            if (pre[i] - pre[q.getFirst()] > ans) {
                ans = pre[i] - pre[q.getFirst()];
            }
        }
        System.out.println(ans);
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/153744.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!