编程之美求数组的子数组之和的最大值

Posted

tags:

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

  一个有N个整数元素的一维数组A[0],A[1],......,A[n-1],这个数组当然有很多子数组,那么子数组的最大值是什么呢?

分析与解法

  我们先明确题意:

  1. 题目说的子数组,是连续的;

  2. 题目只需要求和,并不需要返回子数组的具体位置;

  3. 数组中的元素是整数,所以数组可能包含有正整数、零、负整数;

  4. 子数组不为空。

解法一:枚举

  最简单的办法就是枚举所有的i和j,计算sum[i..j] = A[i]+A[i+1]+...+A[j],遍历所有可能的sum[i..j],找到最大值,时间复杂度为O(N3)。

  注意到sum[i..j] = sum[i..j-1]+A[j],则可以改进求和,使得时间复杂度为O(N2)。

解法二:分治

  给定数组A[0],A[1],......,A[n-1]的最大子段和,分为三种情况:

  (1) 与左半部分,即A[0],A[1],......,A[n/2-1]的最大子段和相同;

  (2) 与右半部分,即A[n/2],......,A[n-1]的最大子段和相同;

  (3) 跨过中间两个元素A[n/2-1]和A[n/2]。

  前两种情况是与原问题相同的规模较小的问题,可以递归求解,第三种情况可以遍历一遍数组即可求得,时间复杂度为O(NlogN)。

解法三:动规

  用变量sum表示以arr[i]结尾的子数组的最大和,则所求最大和ret = max{sum}

  根据如下递推关系,只需要遍历一遍数组即可:

  sum = max{arr[i], arr[i]+sum}

  ret = max{ret, sum}

  参考代码如下所示:

技术分享
 1 #include <iostream>
 2 using namespace std; 
 3 
 4 const int maxn = 1007; 
 5 int arr[maxn]; 
 6 
 7 int maxSum(int arr[], int n)
 8 {
 9     int ret = arr[0], sum = arr[0]; 
10     for (int i = 1; i < n; i++)
11     {
12         sum = (sum > 0) ? (sum + arr[i]) : arr[i]; 
13         ret = (ret > sum) ? ret : sum; 
14     }
15     return ret; 
16 }
17 
18 int main(int argc, char *argv[])
19 {
20     int n; 
21     while (cin >> n)
22     {
23         for (int i = 0; i < n; i++)
24         {
25             cin >> arr[i]; 
26         }
27         int maxsum = maxSum(arr, n); 
28         cout << maxsum << endl;
29     }
30 }
View Code

扩展问题

1. 如果数组是首尾相邻的,也就是我们允许找到一段数字A[i],...,A[n-1],A[0],...,A[j],请使其和最大,怎么办?

  解答:可以分为两种情况:

  (1) 解没有跨过A[n-1]到A[0](原问题);

  (2) 解跨过A[n-1]到A[0]。

  第二种情况又有两种情况,即包含整个数组和不包含整个数组,遍历数组即可求解。

  当然,对于不包含整数数组的情况,相当于从数组中删除A[j+1]...A[i-],这部分的和一定是负的,且是可能找到的子数组的和为负数且绝对值最大的。寻找这样的子数组的问题跟情形(1)是一样的,可以用同样的方法解决。

  最后,取两种情况的最大值即可。

2. 如果题目要求同时返回最大子数组的位置,算法应如何变?还能保持O(N)的时间复杂度么?

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

编程之美 2.14求数组的子数组之和的最大值

编程之美寻找数组中的最大值和最小值

求数组的子数组之和的最大值及扩展问题2

求数组的子数组之和的最大值及扩展问题2

编程之美数组分割

编程之美子数组的最大乘积