最大連續子數組以及拓展


問題描述:給定一個整數數組,數組中可能有正數、負數和零。數組中連續的一個或者多個正數組成一個子數組,每個子數組都有一個和。求所有子數組的和的最大值。例如,若果輸入的數組為{1,-2,3,,10,-4,7,2,-5},和最大的子數組為{3,10,-4,7,2},應輸出該子數組的和18。

 

解題方法一:蠻力枚舉

  用三個for循環三層遍歷,求出數組中每一個子數組的和,最終求出這些子數組和最大的一個值。

參考代碼:

#include <bits/stdc++.h>

using namespace std;

int MaxSubArray( int *a, int n )
{
int maxSum = a[0];
int currSum = 0;
for( int i = 0 ; i < n ; i ++ )
{
for( int j = i ; j < n ; j ++ )
{
for( int k = i ; k <= j ; k ++ )
{
currSum
+=a[k] ;
}
if( currSum > maxSum )
{
maxSum
= currSum ;
}
currSum
= 0;
}
}
return maxSum;
}

int main()
{
int a[8]={1,-2,3,10,-4,7,2,-5};
cout
<<MaxSubArray(a,8);
}

GCC運行結果:

該方法的時間復雜度為O(n^3)

解題方法二:動態規划

  事實上,可以令currSum是以當前元素結尾的最大連續子數組的和,maxSum是全局的最大子數組的和,當往后掃描時,對第j個元素有兩種選擇,要么放入前面找到的子數組,要么作為新的子數組的第一個元素:如果currSum>0,則令currSum加上a[j],如果currsum<0,則currSum被置為當前元素,即currSum=a[j]。

  這相當於,如果設currSum(j)是以j結尾的最大連續子數組的和,那么currSum(j)=max{0,currsum[j-1]}+a[j]。如果maxSum<currSum,則更新maxSum=currSum;否則maxSum保持原值,不更新。

  舉個例子,對於輸入數組為{1,-2,3,10,-4,7,2,-5},那么currSum和maxSum的變化分別為:

currSum: 0 -> 1 -> -1 -> 3 -> 13 -> 9 -> 16 -> 18 -> 13

maxSum:  0 -> 1 -> 1 -> 3 -> 13 -> 13 -> 16 -> 18 -> 18

參考代碼:

#include <bits/stdc++.h>

using namespace std;

int MaxSubArray( int *a , int n )
{
int currSum = 0;
int maxSum = a[0];
for( int j = 0 ; j < n ; j ++ )
{
if( currSum >= 0 )
{
currSum
+= a[j];
}
else
{
currSum
= a[j];
}
if( currSum > maxSum )
{
maxSum
= currSum;
}
}
return maxSum;
}
int main()
{
int a[8]={1,-2,3,10,-4,7,2,-5};
cout
<<MaxSubArray(a,8);
}

GCC運行結果:

該方法從前向后掃描數組一遍,因此該方法的時間復雜度為O(n)。

 

問題變形:

(1)如果要求出最大連續子數組的和,同時要求輸出所求子數組的開始位置和結束位置呢?

  解答:在解題方法一二中只需要設置開始和結束位置,每次調整數值即可。

 解題方法一:

#include <bits/stdc++.h>

using namespace std;

int MaxSubArray( int *a, int n )
{
int maxSum = a[0];
int currSum = 0;
int i,j,k;
int start = 0;
int over = 0;
for(i = 0 ; i < n ; i ++ )
{
for( j = i ; j < n ; j ++ )
{
for( k = i ; k <= j ; k ++ )
{
currSum
+=a[k] ;
}
if( currSum > maxSum )
{
start
= j;
over
= k;
maxSum
= currSum ;
}
currSum
= 0;
}
}
cout
<<"maxSum = "<<maxSum<<" start = "<<start+1<<" over = "<<over+1<<endl;
return maxSum;
}

int main()
{
int a[8]={1,-2,3,10,-4,7,2,-5};
cout
<<MaxSubArray(a,8);
}
View Code

 解題方法二:

#include <bits/stdc++.h>

using namespace std;
int MaxSubArray( int *a , int n )
{
int currSum = 0;
int maxSum = a[0];
int start = 0;
int over = 0;
for( int j = 0 ; j < n ; j ++ )
{
if( currSum >= 0 )
{
over
= j;
currSum
+= a[j];
}
else
{
start
= j ;
currSum
= a[j];
}
if( currSum > maxSum )
{
maxSum
= currSum;
}
}
cout
<<"maxSum = "<<maxSum<<" start = "<<start+1<<" over = "<<over+1<<endl;
return maxSum;
}
int main()
{
int a[8]={1,-2,3,10,-4,7,2,-5};
int start ,over ;
MaxSubArray(a,
8);
}
View Code

(2)如果要求出最大子數組的和,但不要求子數組是連續的呢?

 解答:只需要遍歷數組,如果該數大於0,則sumMAX加上該數。時間復雜度為O(n)

(3)如果數組是二維數組,同樣要求出最大連續子數組的和呢?

解答:將a[0][] a[1][0]...a[n][]分別使用一位數組的方法求解出每個單位數組的最大值,對着n個最大的和進行快速排序找到最大的和

(4)如果是要求出連續子數組的最大乘積呢?

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com