最大子数列之和问题

Posted yifan2016

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大子数列之和问题相关的知识,希望对你有一定的参考价值。

给定一个数组,数组长度为n,数组中每个元素为一个整数(其中有正数,负数,零),求一和最大的子数组

这是一道老生常谈的动态规划问题,也是我大一学算法时遇到的第一道动态规划问题,当时觉得解法非常精妙,从此爱上了算法。

题目的具体解法为是dp[i][0]表示前i项数列不含最后一项(第i项)时的子数列之和的最大值,dp[i][1]表示前i项数列含最后一项的子数列之和最大值

很明显dp[i][0]=max(dp[i-1][1],dp[i-1][1]),dp[i][1]=max(dp[i-1][1]+a[i],a[i]);

但是从上面的状态转移方程可以发现,每一项只与前一项有关,所以状态转移过程中,无需记录多余的状态,只记录该状态的前一种状态即可,所以描述子状态二维数组完全可以使用 dp1 和 dp2 这两个数来代替,从而优化了空间发杂度

最终优化完成的代码如下:

#include<iostream>
#include<string>
using namespace std;
int main(){
    long long dp1,dp2,a[10000],ans;
    int n=1;
    cin>>a[0];
    while(getchar()!=\n){
        cin>>a[n++];
    }
    dp1=-99999999;ans=dp2=a[0];//为避免全为负数
    for(int i=1;i<n;i++){
        dp1=max(dp1,dp2);
        dp2=max(dp2+a[i],a[i]);    
    }
    ans=max(dp1,dp2);    
    cout<<ans<<endl;
    return 0;
}

 

第二,如果上述数列首尾相接,那样的话,其实和原来的问题依然一样,原来的问题可以看做数列在第一个元素和最后一个元素之间比存在一个断口,现在首尾相接,只要枚举一下断口的位置即可,具体代码如下:

#include<iostream>
#include<string>
using namespace std;
int main(){
    long long dp1,dp2,a[10000],ans;
    int n=1;
    cin>>a[0];
    while(getchar()!=\n){
        cin>>a[n++];
    }
    for(int j=0;j<n;j++){
        int k=j;
        dp1=-99999999;ans=dp2=a[k];
        for(int i=1;i<n;i++){
            k++;k%=n;        
            dp1=max(dp1,dp2);
            dp2=max(dp2+a[k],a[k]);    
        }
        ans=max(ans,max(dp1,dp2));    
    }
    cout<<ans<<endl;
    return 0;
}

 

以上是关于最大子数列之和问题的主要内容,如果未能解决你的问题,请参考以下文章

经典算法问题 - 最大连续子数列和

有一个分数序列:求出这个数列的前20项之和。

1049. 数列的片段和(20)

PAT 1049. 数列的片段和

B1049 数列的片段和

经典算法问题 - 最大连续子数列和