【转】编程之美2.14——求数组的子数组之和的最大值

问题:
1. 一个由N个整数元素的一维数组,求其所有子数组中元素和的最大值。
2. 如果数组首尾相邻,也就是允许子数组A[i],...,A[n-1],A[0],...,A[j]存在,求其所有子数组总元素和的最大值。

1. 解法:
我们使用动态规划的思想可以在O(n)的时间内计算出子数组之和最大值。
动态规划问题往往非常灵活,所以在做题的时候不知道如何编写,其实它有很清晰的分析方法,按照这个方法就可以较容易的解决动态规划的问题。具体参考IOI2000 张辰的论文《动态规划的特点及其应用》
阶段:在所有以元素k结尾的子数组中,选出元素和最大的子数组,k=1,2...n。
状态:以元素k结尾的和最大的子数组是包含以元素k-1结尾的和最大的子数组还是就只有元素k这一个元素,即有这两个可选状态。
之所以选择这样的阶段和状态,因为这种选择方式具有无后效性,即阶段k+1(以元素k+1结尾的子数组)只会与阶段k(以元素k结尾的子数组)发生关联,而与其它阶段无关。在得到以每个元素结尾的和最大的子数组之后,只要取其中最大值就是所有子数组中最大的子数组。

#include <iostream> 
#include <algorithm> 
using namespace std; 
 
#define MAXN 1003 
int A[MAXN]; 
 
// 动态规划思想,时间复杂度O(n) 
int Tail[MAXN]; 
 
int main() 
{ 
    int n, i, j, k; 
    cin >> n; 
    for (i=1; i<=n; i++) 
        cin >> A[i]; 
    // 计算以k结尾的子数组之和的最大值,即子数组包含第k个数 
    Tail[1] = A[1]; 
    for (k=2; k<=n; k++) // k个阶段 
        Tail[k] = max(A[k],Tail[k-1]+A[k]); // 只有两个状态 
    // 因为和最大的子数组肯定以某个数结尾,所以取这n个子数组的最大值 
    // 就是和最大的子数组 
    int All = Tail[1]; 
    for (i=2; i<=n; i++) 
        All = max(All, Tail[i]); 
    cout << All; 
} 

虽然这种标准的动态规划方法时间复杂度已经最优了,但它仍要占用O(n)的空间,对于一般的动态规划问题占用较多的空间是不可避免的,但这个问题较简单,仍可以继续优化。我们把All取最大值的操作放入Tail的计算循环中,如下:

Tail[1] = A[1];
All = Tail[1];
for (k=2; k<=n; k++)// k个阶段
{
Tail[k] = max(A[k],Tail[k-1]+A[k]);// 只有两个状态
All = max(All, Tail[k]);
}
由于循环体中只关心当前的Tail[k]和上一个Tail[k-1],可以省去之前所计算出的Tail[1],Tail[2]...Tail[k-2]的数据,如下:
Tail = A[1];
All = Tail;
for (k=2; k<=n; k++)// k个阶段
{
Tail = max(A[k],Tail+A[k]);// 只有两个状态
All = max(All, Tail);
}

最后优化后的代码就是书中最后给出的结果。

2. 解法:
把问题分为两种情况:
(1)最大和子数组没有跨过A[n]到A[1](如问题1)
(2)最大和子数组跨过A[n]到A[1]
对于情况(2),这样的最大和子数组包含两个部分:以A[1]开始的最大和子数组,以及以A[n]结尾的最大和子数组,并且这两个子数组不允许重叠,那么将这两个子数组拼合起来就是情况(2)的解。

#include <iostream> 
#include <algorithm> 
using namespace std; 
 
#define MAXN 1003 
int A[MAXN]; 
 
int main() 
{ 
    int n, i, j, k; 
    cin >> n; 
    for (i=1; i<=n; i++) 
        cin >> A[i]; 
    // 和最大的子数组没有跨过A[n]和A[1] 
    int Tail = A[1]; 
    int All = Tail; 
    for (k=2; k<=n; k++) // k个阶段 
    { 
        Tail = max(A[k],Tail+A[k]); // 只有两个状态 
        All = max(All, Tail); 
    } 
    // 和最大的子数组跨过了A[n]和A[1] 
    int Start = A[1]; 
    for (i=2; i<=n && Start+A[i]>Start; i++) 
            Start += A[i]; 
    Tail = A[n]; 
    for (j=n-1; j>=1 && Tail+A[j]>Tail; j--) 
            Tail += A[j]; 
    if (i<j && Start+Tail > All) 
        All = Start+Tail; 
    cout << All; 
} 

转自http://www.2cto.com/kf/201207/139312.html

posted @ 2013-11-22 16:10  DKMP  阅读(272)  评论(0编辑  收藏  举报