最大連續子序列之和,最大連續子序列乘積


  最大連續子序列之和問題描述為:數組中里有正數也有負數,連續的一個或多個整數組成一個子數組,每個子數組都有一個和,求所有子數組的和的最大值。分析,對數組a進行一遍掃描,sum[i] 為前i個元素中,包含第i個元素且和最大的連續子數組,MaxSum保存當前子數組中最大和,對於a[i+1]來說,sum[i+1] = sum[i]+a[i+1],此時如果sum[i+1]<0,那么sum需要重新賦0,從i+1之后開始累加,如果sum[i+1]>0,那么MaxSum = max(MaxSum, Sum[i+1])。代碼如下:

 1 int maxSum(int *nArray, int nSize, int &nBegin, int &nEnd)  2 {  3     int nSum = 0, nMaxSum = 0;  4     int nNewBegin = 0;    //記錄新的開始下標
 5     nBegin = nEnd = 0;    //記錄最大連續子數組和的起始於結束下標
 6     for(int i=0; i!=nSize; i++)  7  {  8  
 9         nSum += nArray[i]; 10         if(nSum >= nMaxSum) 11  { 12             nMaxSum = nSum; 13             nBegin = nNewBegin; 14             nEnd = i; 15  } 16         else if(nSum < 0) 17  { 18             nSum = 0; 19             nNewBegin = i+1; 20  } 21  } 22  
23     return nMaxSum; 24 } 25 
26 int _tmain(int argc, _TCHAR* argv[]) 27 { 28     int Array[5] = {2, -3, 4, 5, -100}; 29     int nBegin = 0, nEnd = 0; 30     int nMaxSum = maxSum(Array, sizeof(Array)/sizeof(*Array), nBegin, nEnd); 31     cout<<nMaxSum<<endl; 32     cout<<"開始下標為["<<nBegin<<"], 結束下標["<<nEnd<<"]"<<endl; 33 
34     return 0; 35 }

  最大連續子序列乘積,問題描述和前面求最大連續子序列之和類似:給一個浮點數序列,取最大乘積連續子串的值。這里需要重點注意的是乘積需要注意正負號,需要考慮到有偶數個的情況,所以計算時,不止要保存當前最大乘積,也要保存當前最小乘積。代碼如下:

 1 double maxProduct(double a[], int nLen, int &nBegin, int &nEnd)  2 {  3     int nNewBegin = 0;  4     nBegin = nEnd =0;  5 
 6     double dCurMax = 1.0f;  7     double dCurMin = 1.0f;  8     double dMax = 1.0f;  9     double dMin = 1.0f; 10     for(int i=0; i!=nLen; i++) 11  { 12         dCurMax *= a[i]; 13         dCurMin *= a[i]; 14         cout<<"dCurMax = "<<dCurMax<<", dCurMin = "<<dCurMin<<endl; 15         if(dCurMax > dMax) 16  { 17             dMax = dCurMax; 18             nBegin = nNewBegin; 19             nEnd = i; 20  } 21         if(dCurMin > dMax) 22  { 23             dMax = dCurMin; 24             nBegin = nNewBegin; 25             nEnd = i; 26  } 27         if(dCurMax < dMin) 28  { 29             dMin = dCurMax; 30  } 31         if(dCurMin < dMin) 32  { 33             dMin = dCurMin; 34  } 35 
36         if(dCurMax == 0 || dCurMin == 0) 37  { 38             dCurMax = dCurMin = 1; 39             nNewBegin = i+1; 40  } 41 
42         cout<<"dMax = "<<dMax<<", dMin = "<<dMin<<endl; 43         cout<<"begin = "<<nBegin<<", end = "<<nEnd<<endl; 44  } 45 
46     return dMax; 47 } 48 
49 int _tmain(int argc, _TCHAR* argv[]) 50 { 51     double a[] = { -2.5, 4, 0, 3, 0.5, 8, -2, -2}; 52     int nBegin, nEnd; 53     int max = maxProduct(a, sizeof(a)/sizeof(*a), nBegin, nEnd); 54     cout<<max<<endl; 55     cout<<nBegin<<"  "<<nEnd<<endl; 56 
57     return 0; 58 }

  在網上看到使用動態規划的算法來處理此題目。假設從數組開頭 i 到結尾 j 的范圍,求出所有元素為結尾的子序列最大值,取其中最大的那個即為所求的最大連續子序列乘積。假設max(i, k)表示從數組 i 開始到 j 結束的范圍內,包含 j 作為結尾的最大連續子序列乘積,注意不一定以 i 作為起始,問題可以概括為max = max(max(i, i), max(i, i+1), ……, max(a, k), ……., max(i, j)) 。那么對於max(i, k)后面的max(i, k+1)來說,會有如下幾種情況:

  1. max(i, k)和a[k+1]均為正數,且max(i, k)*a[k+1] > max(i, k),那么有 max(i, k+1) = max(i, k) * a[k+1]
  2. max(i, k)和a[k+1]一正一負,如果max(i, k)>0,a[k+1] < 0,那么乘積<0,而max(i, k+1)要包含a[k+1],所以max(i, k+1) = a[k+1],反之亦然
  3. max(i, k)為正數,a[k+1]為負數,不過max(i, k)之前相連的序列里有負數,那么前面包含負數的這個序列,必然是一個前面序列的最小值,此時max(i, k+1) = min(i, k) * a[k+1]

  概括起來,包含第k+1個元素為結尾的序列最大乘積應該取自上述三種情況之一:max(i, k+1) = max(max(i, k) * a[k+1],  a[k+1], min(i, k) * a[k+1])。

  按照同樣的道理,我們求得的包含k+1在內結尾的最小乘積序列為:min(i, k+1) = min(min(i, k) * a[k+1], a[k+1], max(i, k) * a[k+1])。

  代碼如下:

 1 int maxProduct_DP(double a[], int n)  2 {  3     double maxCur = 1.0f;  4     double minCur = 1.0f;  5     double maxTmp = maxCur;  6     double minTmp = minCur;  7     double result = 0.0f;  8 
 9     for(int i=0; i!=n; i++) 10  { 11         maxTmp = max(maxCur*a[i], max(a[i], minCur*a[i])); 12         minTmp = min(maxCur*a[i], min(a[i], minCur*a[i])); 13 
14         maxCur = maxTmp; 15         minCur = minTmp; 16 
17         result = max(result, maxCur); 18  } 19 
20     return result; 21 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM