poj 2479 最大连续子段和 dp算法

一、文章来由

晚上一水~~poj2479,一道简单的dp问题,回顾一下动态规划

二、求最大子段和

这道题是求一个序列中的两个子段的最大和,是求纯的最大和的一个变体,例如题目中给出的例子
1 -1 2 2 3 -3 4 -4 5 -5
In the sample, we choose {2,2,3,-3,4} and {5}, then we can get the answer.
答案是 13

如何求一个序列的子段和:

int MaxSub (int a[],int N)//此为只需要求最大的和,时间复杂度是O(n)  
{  
    int *dp = new int(N);
    int max, i;

    max = dp[0] = a[0];
    for (i=1; i<N; i++)
    {
        if (dp[i-1] > 0)
            dp[i] = dp[i-1] + a[i];
        else
            dp[i] = a[i];

        if (dp[i] > max)
            max = dp[i];
    }
    delete dp;
    return max;
}

分析:

思路很清晰的写法,每次都判断,当前dp值(即子串当前最大值),如例子中的:
1 -1 2 2 3 -3 4 -4 5 -5
1 0 2 4 7 4 8 4 9 4

这样就很清楚了~~

三、解题报告

先上AC代码:

#include <iostream>
#include <limits>
using namespace std;

#define MAXN 50002
int map[MAXN];
int Ldp[MAXN];
int Rdp[MAXN];

void MaxLSub (int a[],int N)//此为只需要求最大的和,时间复杂度是O(n)  
{
    int i;

    Ldp[0] = a[0];
    for (i=1; i<N; i++)
    {
        if (Ldp[i-1] > 0)
            Ldp[i] = Ldp[i-1] + a[i];
        else
            Ldp[i] = a[i];
    }

    for(i=1; i<N; i++)
        Ldp[i] = Ldp[i]<Ldp[i-1]?Ldp[i-1]:Ldp[i];
}

void MaxRSub (int a[],int N)//此为只需要求最大的和,时间复杂度是O(n)  
{
    int i;

    Rdp[N-1] = a[N-1];
    for (i=N-2; i>-1; i--)
    {
        if(Rdp[i+1]>0)
            Rdp[i] = Rdp[i+1]+a[i];
        else
            Rdp[i] = a[i];
    }

    for (i=N-2; i>-1; i--)
        Rdp[i] = Rdp[i]<Rdp[i+1]?Rdp[i+1]:Rdp[i];
}

int main()
{
    int T,n;
    cin>>T;

    while (T--)
    {
        scanf("%d",&n);
        //cin>>n;
        for (int i=0;i<n;++i)
        {
            scanf("%d",&map[i]);
            //cin>>map[i];
        }

        MaxLSub(map,n); //生成dp
        MaxRSub(map,n);

        int res=INT_MIN;
        for (int i=1;i<=n-1;++i)
        {
            int tmp = Ldp[i-1]+Rdp[i];
            res = tmp>res?tmp:res;
        }

        cout<<res<<endl;
    }

    return 0;
}

但是这题用暴力的 MaxSub(map,i)+MaxSub(map+i,n-i) 去做,是没有用的,会TLE,后来我将cin换成scanf,也是不行的,再后来用了这个方法:


int findMax(int lb,int rb)
{
    int max=INT_MIN;
    for (int i=lb;i<=rb;++i)
    {
        max = dp[i]>max?dp[i]:max;
    }

    return max;
}

int main()
{
/... ...
        for (int i=1;i<=n-1;++i)
        {
            //int tmp = MaxSub(map,i)+MaxSub(map+i,n-i); //n^2

            int tmp = findMax(0,i) + findMax(i+1,n)-dp[i-1];
            res = tmp>res?tmp:res;
        }
/... ...
}

究其原因是,用 MaxSub(map,i)+MaxSub(map+i,n-i) 纯暴力方法,是 O(n^2) 的复杂度,后来改成 findMax(0,i) + findMax(i+1,n)-dp[i-1] 其实依然是 O(n^2) 的复杂度。。

AC代码是用从左往右,找出常规的最大子串,然后用

for(i=1; i<N; i++)
        Ldp[i] = Ldp[i]<Ldp[i-1]?Ldp[i-1]:Ldp[i];

求出从左往右,当前点的最大值,如果用例子中的值就是:
1 -1 2 2 3 -3 4 -4 5 -5
1 0 2 4 7 4 8 4 9 4
1 1 2 4 7 7 8 8 9 9

保证从左往右,一定是变大,找到一个点就可以找到这个点左边的最大值~~
同理,可以计算从右往左的,一个点右边的最大值,这样就用空间换时间了,但是即使这样,也只跑出了438MS。。。

而且将scanf换成cout,又TLE了

—END—


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值