51Nod - 1050 单调队列

题意:

N个整数组成的循环序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续的子段和的最大值(循环序列是指n个数围成一个圈,因此需要考虑a[n-1],a[n],a[1],a[2]这样的序列)。当所给的整数均为负数时和为0。
例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
Input
第1行:整数序列的长度N(2 <= N <= 50000)
第2 - N+1行:N个整数 (-10^9 <= S[i] <= 10^9)
Output
输出循环数组的最大子段和。
Input示例
6
-2
11
-4
13
-5
-2
Output示例
20

思路:

题目其实就是求出下标i,j,使得sum[j]-sum[i]最大,只要枚举每个j然后找到符合的最小的sum[i]就可以。这里既然是循环的数组,就需要扩充两倍长度,但是所求的结果显然不能j-i超过n,所以考虑单调队列,front端到back端保证单调sum[x]单调递增。每次枚举先把超过长度的pop掉,然后更新,再维护单调性地插入sum[i]即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 5e4 + 10;

ll a[MAXN * 2], sum[MAXN * 2];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%I64d", &a[i]);
        a[n + i] = a[i];
    }
    sum[0] = 0;
    for (int i = 1; i <= 2 * n; i++)
        sum[i] = sum[i - 1] + a[i];
    ll ans = 0;
    deque <int> que;
    que.push_back(0);
    for (int i = 1; i <= 2 * n; i++) {
        while (!que.empty() && i - que.front() > n) que.pop_front();
        ans = max(ans, sum[i] - sum[que.front()]);
        while (!que.empty() && sum[i] < sum[que.back()]) que.pop_back();
        que.push_back(i);
    }
    printf("%I64d\n", ans);
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值