最大子序和 单调队列

题目链接

https://www.acwing.com/problem/content/137/

输入一个长度为n的整数序列,从中找出一段长度不超过m的连续子序列,使得子序列中所有数的和最大。

输入格式

第一行输入两个整数n,m。

第二行输入n个数,代表长度为n的整数序列。

同一行数之间用空格隔开。

输出格式

输出一个整数,代表该序列的最大子序和。

数据范围

1≤n,m≤300000

输入样例:

6 4
1 -3 5 1 -2 3

输出样例:

7

题解 

题解参考李煜东的算法竞赛进阶指南

      用sum数组保存前缀和,那么区间和就转化为两个个前缀和相减。

       首先我们枚举右端点i,当i固定时,问题就变为:找到一个左端点j,其中j属于[j-m,i-m]并且是sum[j]最小。

      不妨比较一下任意两个位置j和k,如果k<j<i并且sum[k]>=sum[j],那么对于所有大于等于i的右端点,k永远不会成为最优选择,这是因为不但sum[k]不小于sum[j],而且j离i更近,长度更不容易超过m,即j的生存能力比k更强。所以当j出现后,k是一个完全无用的位置。

    以上比较告诉我们,可能成为最优选择的策略集合一点哪个是一个“下标位置递增,对应前缀和sum的值也递增”的序列。我们可以用一个碎裂保存这个序列。随着右端点变从前向后扫描,我们对每个i执行以下三个步骤:

   1.判断对头决策与i的距离是否超过M的范围,若超出则出队。

   2.此时对头就是右端点i时,左端点j的最优选择。

   3.不断删除队尾决策,直到队尾对应的sum值小于是sum[j].然后把i作为一个新的决策入队。

代码

用q数组模拟队列,队列里存的是下标,head是队头,tail是队尾。队列里的元素是单调递增的。head对应的值最小

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=3e5+5;
typedef long long ll;
int sum[maxn];
int q[maxn];
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>sum[i];
        sum[i]+=sum[i-1];
    }
    int head=1,tail=1;
    q[head]=0;
    int ans=sum[1];
    for(int i=1;i<=n;i++){
        if(head<=tail&&q[head]<i-m) head++;   //步骤1
        ans=max(ans,sum[i]-sum[q[head]]);       //步骤2
        //步骤3
        while(head<=tail&&sum[q[tail]]>=sum[i]) tail--;
        q[++tail]=i;
    }
    cout<<ans<<endl;
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值