最大子序和

 

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

 

通俗做法是n2的,就是求前缀和,sum[i]-sum[j]的最大值就是1~n枚举每个sum[i]也就是右端点,对于每个数找一个范围内最小的sum[j]左端点,然后求最大值。

过程中可以优化,对于每个右端点来说,没必要遍历一遍它的所有的左端点去找最小值,我们可以维护一个从左往右单调递增的序列,可以直接确定这个左端点最小值就是这个序列最左的值。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=3e5+10;
int q[maxn];
ll sum[maxn];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
    {
        ll t;
        scanf("%lld",&t);
        sum[i]=sum[i-1]+t;
    }
    int head=1;
    int tail=0;
    ll maxx=-1e18;
    for(int i=1; i<=n; i++)
    {
        while((i-q[head])>m)
        {
            head++;
        }
        maxx=max(maxx,sum[i]-sum[q[head]]);
        while(head<=tail&&sum[i]<sum[q[tail]])
        {
            tail--;
        }
        q[++tail]=i;
    }
    printf("%lld",maxx);
}

 

倒着写了一遍,更理解含义了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=3e5+10;
int q[maxn];
ll sum[maxn];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m); // 0~n
    for(int i=1; i<=n; i++)
    {
        ll t;
        scanf("%lld",&t);
        sum[i]=sum[i-1]+t;
    }
    int head=1;
    int tail=0;
    ll maxx=-1e18;
    q[head]=n;
    for(int i=n; i>=0; i--)
    {
        while((q[head]-i)>m)
        {
            head++;
        }
        maxx=max(maxx,sum[q[head]]-sum[i]);
        while(sum[i]>sum[q[tail]]&&head<=tail)
        {
            tail--;
        }
        q[++tail]=i;
    }
    printf("%lld",maxx);
}

 

posted @ 2019-07-14 17:31  paranoid。  阅读(118)  评论(0编辑  收藏  举报