一个有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 }
扩展问题
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)的时间复杂度么?