最大连续区间和

问题概述

这是一个经典的问题。

给定一个长度为n的序列a[1],a[2]...a[n-1],a[n]

求一个连续的子序列 a[i],a[i+1]...a[j-1],a[j],使得a[i]+a[i+1]...a[j-1]+a[j]最大。

暴力的方法就是双重循环枚举左右端点,然后直接找最大的就好了。 但是算法的复杂度是 O(N^2) 不够优秀

 

动态规划解法  复杂度O(n) 

我们让 dp[ i ] 代表以 a[ i ] 为结束的最大连续子段和

因为是以a[ i ]为结束且是连续子段  那么

dp[ i ] 要么就是  a[ i ]本身

          要么 就是a[ i ] + 以a[ i-1 ]为结束的最大连续字段和  也就是 a[ i ] + dp[ i - 1 ]

所以 状态转移方程出来了      dp[i] = max( A[i], dp[i-1]+A[i] )

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);//输入
    dp[0]=0;
    for(int i=1;i<=n;i++){//状态转移方程
        dp[i]=max(a[i],dp[i-1]+a[i]);
    }
    ll maxn=0;
    for(int i=1;i<=n;i++){//遍历找最大值
        maxn=max(dp[i],maxn);
    }
    printf("%lld
",maxn);
}

这个算法的常数还是比较大的,我们可以优化下常数

ll a[maxn];
ll n,ans,dp;
int main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        dp=max(a[i],dp+a[i]);
        ans=max(dp,ans);
    }
    printf("%lld
",ans);
}

如果我们还想知道这个具体的最大连续区间的左右端点是什么

 1 int a[maxn];
 2 int main() {
 3     //freopen("../in.txt","r",stdin);
 4     int t;
 5     scanf("%d",&t);
 6     int num = 1;
 7     while (t--) {
 8         int n;
 9         scanf("%d",&n);
10         int ans = -INF,dp = 0,l = 1,r = 1,k = 1;
11         for (int i = 1;i <= n;i++) {
12             scanf("%d",&a[i]);
13             if (dp >= 0) {
14                 dp += a[i];
15             }
16             else {
17                 dp = a[i];
18                 k = i;
19             }
20             if (dp > ans) {
21                 ans =  dp;
22                 l = k;
23                 r = i;
24             }
25         }
26         printf("Case %d:
",num++);
27         printf("%d %d %d
",ans,l,r);
28         if (t != 0)
29             printf("
");
30     }
31     return 0;
32 }
原文地址:https://www.cnblogs.com/-Ackerman/p/12180830.html