51nod 1050 循环数组最大子段和 单调队列优化DP

题目链接: http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1050

这个呢,这个题之前 求一遍最大值  然后求一遍最小值

最后结果 res = max( MAX,  SUM - MIN );

但是 这种题如果 要求 变成 最长长度为len的最大子段和,这种思路就会受限制

换一种想法, 让你求最大长度为len的最大字段和 那么 你可以维护一个前缀和 

然后结果就是 max( sum[i] - min(sum[j]) , i-len <= j <= i-1 && 1<=i <= 2*n)

然后这么看来 是n^2的dp,那么如何优化呢。

可以预处理  长度为len的区间的最小值  然后对于每个i 查询前面区间长度为 len 的最小值 然后 sum[i] - sum[j]即可 ,(类似RMQ,或者线段树,树状数组都可以做 )复杂度 O(nlogn)

也可以 维护单调队列  单调队列要保证 两点:

1.  队内元素的位置 要符合 i的区间要求 即 i-len <=que[j]<=i-1

2. 单调性, 队列内的元素 尽可能小,维护一个单调非递增队列

#include <iostream>
#include <cstdio>
#include <string.h>
#include <string>
#include <algorithm>
using namespace std;
const int N = 5e4+10;
typedef long long ll;

int n; ll s[N<<1],sum[N<<1];
int q[N<<1];

int main ()
{
    cin >> n; int len = n;
    for(int i=1;i<=n;i++) {
        cin >> s[i];
        s[i+n] = s[i];
    }
    n<<=1;
    for(int i=1;i<=n;i++) {
        sum[i] = sum[i-1] + s[i];
    }
    ll res = 0;
    int st=0,ed=0;

    for(int i=1; i<=n; i++) {
        if(i <= len)
            res = max(res, sum[i]); 
        // sum[i] - min(sum[j]),  (i-len<=j<=i-1)
        while (st < ed && q[st] < i-len) 
            st++; 
        res = max(res, sum[i] - sum[q[st]]);
        while (st < ed && sum[i] <= sum[q[ed-1]]) 
            ed--;
        q[ed++] = i;
    }
    cout << res <<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/Draymonder/p/9536681.html