题目
输入一个整形数组,数组里有正数也有负数。数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n).
基本思想:
利用动态规划求解,递归公式如下,其中f(i)表示以第i个数字结尾的子数组的最大和。
公式意义如下:
如果当以第i-1个数字结尾的子数组中所有数字的和小于0,则如果把这个负数与第i个数累加,则得到的结果比第i个数本身还要小,那么这时应该更新f(i)为第i个数字本身,即pData[i]。反之,如果以第i-1个数字结尾的子数组中所有数字的和大于0,则f(i)应该更新为第i个数字加上第i-1个数字结尾的子数组中所有数字的和。这样扫一遍数组之后就可以得到max[f(i)]。
代码如下:
int FindGreatestSumOfSubArray(vector<int> array) {
if(array.empty()) return 0;
int nCurSum=array[0];
int res=nCurSum;
for(int i=1;i<array.size();++i)
{
nCurSum=(nCurSum>0)?array[i]+nCurSum:array[i];
res=(nCurSum>res)?nCurSum:res;
}
return res;
}
拓展1:最大和子矩阵
题目:
基本思想:
在上面求连续子数组的最大和时候,其实所求的解可以等价穷举了所有可能连续子数组后得出了最大的连续子数组的和。
1. 那么在本题中,我们可以将矩阵的每一列看成一个数(其实是一个数组),首先求出第一行数组的连续子数组的最大和,即[-90,48,78],然后把第二行[64,-40,64]分别按列累加到第一行得到数组[-26,8,142],然后求出该数组的最大累加和。依次类推,依次累加到最后一行。
2. 接下来我们对矩阵的每一行,重复第1步的过程,这样就可以得出子矩阵最大累加和。
3. 以上1,2步骤也就是相当于枚举了所有子矩阵的最大累加和。
代码如下:
int sumOfSubMatrix(vector<vector<int> > mat, int n) {
// write code here
if(n==0) return -1;
int max=INT_MIN;
int cur=0;
int *s=new int[n];//累加数组
for(int i=0;i<n;i++){
for(int i=0;i<n;++i)
s[i]=0;//数组清零
for(int j=i;j<n;j++){
cur=0;
for(int k=0;k<n;++k){
s[k]+=mat[j][k];
cur+=s[k];
max=cur>max?cur:max;
cur=cur<0?0:cur;
}
}
}
delete []s;
return max;
}
拓展2:不相交数组的最大和
题目:
给定一个长度为N的整数数组a,求不重叠的两个子数组的和的最大值。
如a[6]={1, 2, -4, 3, 2, -5}。所取的子数组分别为{1,2},{3, 2}时,两个子数组的和最大,为3+5=8。
基本思想:
首先利用两个数组array1和array2,array1保存从0到第i个数字结尾的连续子数组的最大和, array2保存从第i个数字到结尾len-1的连续子数组的最大和。那么将数组分别从第i个数字为分割线把数组分割成arr[0..i]跟arr[i..len-1],那么array[i]+array[i+1]就是所求值。
代码如下:
int twoSubArrayMaxSum(vector<int> arr){
if(arr.size()<=0)
return 0;
vector<int> rArray;
rArray.reserve(arr.size());//这里只开了一个数组,array1跟array2的两个过程其实就是正逆过程,在结算array2的时候可以直接利用array1的结果
int max=INT_MIN;
int cur=0;
for(int i=arr.size()-1;i>0;i--){
cur+=arr[i];
max=cur>max?cur:max;
rArray[i]=max;
cur=cur<0?0:cur;
}
int res=INT_MIN;
max=INT_MIN;
cur=0;
for(int i=0;i<arr.size()-1;i++){
cur+=arr[i];
max=cur>max?cur:max;
res=(res>(max+rArray[i+1]))?res:(max+rArray[i+1]);
cur=cur<0?0:cur;
}
return res;
}