【动态规划】最大数列

最大数列

(sequence.pas/c/cpp)

 

【问题描述】

有一个N项的数列a1,a2 ... aN (|ai| <=10000, 1 <= i <= N)。S定义为

你的任务是求S的值,即为求一个序列的两个不相交子序列的最大和。

 

【输入文件】

 

输入文件sequence.in的第一行是一个整数N(2 <= N <=100000),表示数列的项数。第二行有n个整数,用空格分隔,第i个整数Ai(|Ai| <=10000)是第i位数。

 

【输出文件】

 

输出文件sequence.out包括一行,这一行只包含一个整数,就是S。

 

【样例输入】

 

5

-5 9 -5 11 20

 

【样例输出】

 

40

 

【数据规模】

 

对于30%的数据,保证有n <= 80

对于70%的数据,保证有n <= 10000

对于全部的数据,保证有 n <= 100000


正反两个rmq,然后递推求出1到i得最优值和n到i得最优质,最后枚举中转点。

#include <cstdio>
#include <string>
#include <cstring>

long min(long a,long b)
{
    return a<b?a:b;
}
long max(long a,long b)
{
    return a>b?a:b;
}
long n;
long h[100010];
long h2[100010];
long g[100010];
long g2[100010];
long f[100010][50];
long f2[100010][50];
long sum[100010];
long sum2[100010];
long a[100010];

inline long getint()
{
    long rs=0;bool sgn=1;char tmp;
    do tmp = getchar();
    while (!isdigit(tmp)&&tmp-'-');
    if (tmp == '-'){tmp=getchar();sgn=0;}
    do rs=(rs<<3)+(rs<<1)+tmp-'0';
    while (isdigit(tmp=getchar()));
    return sgn?rs:-rs;    
}

long lower(long a,long b)
{
    if (sum[a] < sum[b])
        return a;
    return b;
}
long lower2(long a,long b)
{
    if (sum2[a] < sum2[b])
        return a;
    return b;
}

inline long lg2(long a)
{
    long rs=0;
    while (a >>= 1)
        rs ++;
    return rs;
}

void rmq_init()
{
    for (long i=1;i<n+1;i++)
    {
        f[i][0] = i;
        f2[i][0] = i;
    }
    long rr = lg2(n);
    for (long j=1;j<rr+1;j++)
    {
        for (long i=1;i+(1<<j)-1<n+1;i++)
        {
            f[i][j] = lower(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }

    for (long j=1;j<rr+1;j++)
    {
        for (long i=n;i-(1<<j)+1>0;i--)
        {
            f2[i][j] = lower2(f2[i][j-1],f2[i-(1<<(j-1))][j-1]);
        }
    }
}


inline long rmq(long l,long r)
{
    if (r < l)
        return 0;
    long m = lg2(r-l+1);
    return lower(f[l][m],f[r-(1<<m)+1][m]);
}
inline long rmq2(long l,long r)
{
    if (r < l)
        return 0;
    long m = lg2(r-l+1);
    return lower2(f2[r][m],f2[l+(1<<m)-1][m]);
}

int main()
{
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);

    n = getint();
    for (long i=1;i<n+1;i++)
    {
        a[i] = getint();
        sum[i] = sum[i-1]+a[i];
    }
    for (long i=n;i>0;i--)
    {
        sum2[i] = sum2[i+1]+a[i];
    }

    rmq_init();

    for (long i=1;i<n+1;i++)
    {
        g[i] = sum[i] - min(0,sum[rmq(1,i-1)]);
        g2[i] = sum2[i] - min(0,sum2[rmq2(i+1,n)]);
    }

    h[0] = -0x7f7f7f7f;
    h2[n+1] = -0x7f7f7f7f;

    for (long i=1;i<n+1;i++)
    {
        h[i] = max(h[i-1],g[i]);
    }
    for (long i=n;i>0;i--)
    {
        h2[i] = max(h2[i+1],g2[i]);
    }

    long ans = -0x7f7f7f7f;
    for (long i=1;i<n;i++)
    {
        ans = max(ans,h[i]+h2[i+1]);
    }

    printf("%ld",ans);

    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值