POJ - 2479 《Maximum sum》 【线性DP】

2 篇文章 0 订阅
1 篇文章 0 订阅

Description

求一个数列中两个不相交的子段和,要求和最大,输出最大值。

Input

The input consists of T(<=30) test cases. The number of test cases (T) is given in the first line of the input. 
Each test case contains two lines. The first line is an integer n(2<=n<=50000). The second line contains n integers: a1, a2, ..., an. (|ai| <= 10000).There is an empty line after each case.

Output

Print exactly one line for each test case. The line should contain the integer d(A).

Sample Input

1

10
1 -1 2 2 3 -3 4 -4 5 -5

Sample Output

13

【FinkyS】

由于是要找两个不相交的子段,所以第一步先选定数列中的一个元素作为分界点。比如第i个元素,那么以选择第i个元素基础,此时要找的最大子段合就是第i个元素左边的最大子段以及右边的最大子段之和。假设用left[i]和right[i]来记录,那么left[i]从最左边即i=0开始,此时很容易知道left[0]=A[0],然后向右扫描即此时i=1,此时left[1]的值为left[0]和必定包含A[i]的左最大子段中较大的那一个,然后这里就遇到一个问题:要计算left[i]的值还必须知道必定包含A[i]的左最大子段的值那么 先放弃直接找到最大字段,先用left[i]记录必定包含A[i]的最大左子段。经过观察可以知道:left[0]=A[0],left[1]=max{A[1],left[0]+A[1]}->left[i]=max{A[i],left[i-1]+A[i]},right[i]同理。然后左右扫描一次就可以记录下所有left[i]跟right[i]了。那么此时就可以解决上一个问题了。left[0]=A[0],left[1]=max{left[1],left[0]}->left[i]=max{left[i-1],left[i]};同理可知道:right[i]=max[right[i],right[i+1]];最后就可以得到最优解为for(int i=0,i<n-1;i++)MAX=max{MAX,left[i]+right[i+1]};

【Sky】

1.选定数列中的一个元素作为左右子段分界点。

2.以第i个元素A[i]为基础,此时的最优解为其最大左子段以及最大右子段之和。

3.用left[i]、right[i]记录最大(左/右)子段。

4.观察可知,left[0]=A[0],left[1]=max{left[0],必定包含A[1]的最大左子段}

5.遇到一个问题 要计算left[i]的值还必须知道必定包含A[i]的最大子段的值。

6.先放弃直接找到最大字段,先用left[i]记录必定包含A[i]的最大左子段。

7.观察可知,left[0]=A[0],left[1]=max{left[0]+A[1],A[1]} -> left[i]=max{left[i-1]+A[i],A[i]}。 同理  right[i]=max{right[i+1]+A[i],A[i]}

8.扫描一次记录所有left[i]、right[i].

9.回到上一个问题,用left[i]、right[i]记录最大(左/右)子段。

10.观察可知,left[0]=A[0],left[1]=max{left[0],left[1]},left[i]=max{left[i-1],left[i]};同理 right[i]=max{right[i+1],right[i]}

11.最优解MAX为:for(int i=0,i<n-1;i++)MAX=max{MAX,left[i]+right[i+1]};

【实现代码】

#include<cstdio>
#include<cstdlib>
#include<cstring>
int A[50050];
int left[50050];
int right[50050];
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    /*
    char STD[102400]="";
    if(freopen("in.txt","r",stdin)==NULL)freopen("CON","r",stdin);
    else
    {
        fread(STD,1,1024,stdin);
        printf("===input===\n%s\n===output===\n",STD);
        freopen("in.txt","r",stdin);
    }
    */
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)scanf("%d",&A[i]);
        for(int i=0;i<n;i++)
        {
            left[i]=A[i];
            if(i!=0&&left[i-1]>0)left[i]+=left[i-1];
            if(i>1)left[i-1]=max(left[i-1],left[i-2]);
        }
        for(int i=n-1;i>=0;i--)
        {
            right[i]=A[i];
            if(i!=n-1&&right[i+1]>0)right[i]+=right[i+1];
            if(i<n-2)right[i+1]=max(right[i+1],right[i+2]);
        }
        int MAX=0;
        for(int i=0;i<n-1;i++)
        {
            if(i==0)MAX=left[i]+right[i+1];
            else MAX=max(MAX,left[i]+right[i+1]);
        }
        printf("%d\n",MAX);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值