码迷,mamicode.com
首页 > 其他好文 > 详细

最大子序和

时间:2019-07-14 18:12:10      阅读:94      评论:0      收藏:0      [点我收藏+]

标签:can   win   范围   遍历   its   ref   style   枚举   int   

 

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);
}

 

最大子序和

标签:can   win   范围   遍历   its   ref   style   枚举   int   

原文地址:https://www.cnblogs.com/dongdong25800/p/11184944.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!