【最大连续子段和】单调队列 + 前缀和优化

题目描述

输入一个长度为 n 的整数序列(A1,A2,……,An),从中找出一段连续的长度不超过 M 的子序列,使得这个序列的和最大。

输入格式

第一行两个数 n,m
第二行由空格隔开的 n 个整数,即 A1 ~ An 。

输出格式

输出一个整数,即最大连续子序列之和。

样例数据 1

输入  [复制]

4 2 
-1 3 –10 20

输出

20

备注

【数据范围】

30% 的数据:n<=3000,m<=100
100% 的数据 1<=N,M<=1000000,-maxlongint<=ai<=maxlongint

题目分析

 看到了要求一个区间的和,可以想到用前缀和来表示。那么以点(i)结尾的最大连续字段和就为$sum[i] - min\{sum[j]\} (1 <= j < i)$

这里又出现了单调队列的形式:维护前缀和的单调递增序列,入队维护单调性,队首即为最小值,最后比较选出最大值。

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e6 + 5;
int n, m;
int que[N], head, tail;
long long ans, val[N];

inline int read(){
    int i = 0, f = 1; char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        i = (i << 3) + (i << 1) + (ch - '0');
    return i * f;
}

inline long long readL(){
    long long i = 0, f = 1; char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        i = (i << 3) + (i << 1) + (ch - '0');
    return i * f;
}

inline void wr(long long x){
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) wr(x / 10);
    putchar(x % 10 + '0');
}

int main(){
    n = read(), m = read();
    for(int i = 1; i <= n; i++) val[i] = readL() + val[i - 1];
    que[head = tail = 1] = 1, ans = val[1];
    for(int i = 2; i <= n; i++){
        while(head <= tail && val[que[tail]] > val[i]) tail--;
        que[++tail] = i;
        while(que[head] < i - m  && head <= tail) head++;
        ans = max(ans, val[i] - val[que[head]]);
    }
    wr(ans);
    return 0;
}
posted @ 2017-07-29 09:41  CzYoL  阅读(725)  评论(0编辑  收藏  举报