最大連續子串行和問題
給定k個整數的序列,其任意連續子串行可表示為,其中 1 <= i <= j <= k。最大連續子串行是所有連續子序中元素和最大的乙個,例如給定序列,其最大連續子串行為,最大連續子串行和即為20。
注:為方便起見,如果所有整數均為負數,則最大子串行和為0。
解決這樣乙個問題是乙個很有趣的過程,我們可以嘗試著從複雜度比較高的演算法一步一步地推出複雜度較低的演算法。
演算法一:
時間複雜度:o(n^3)
其**:
int maxsubsequence(constint a, int
n)
if(thissum >maxsum)
maxsum =thissum;}}
return
maxsum;
}
對於此種演算法,其主要方法是窮舉法,即求出該序列所有子串行的序列和,然後取最大值即可。
演算法二:
時間複雜度:o(n^2)
其**:
int maxsubsequence(constint a, int
n) }
return
maxsum;
}
對於這種方法,歸根究底還是屬於窮舉法,其間接地求出了所有的連續子串行的和,然後取最大值即可。
那麼,這裡,我們需要對比一下前面兩種演算法,為什麼同樣都是窮舉法,但演算法一的時間複雜度遠高於演算法二的時間複雜度?
演算法二相較於演算法一,其優化主要體現在減少了很多重複的操作。
對於a-b-c-d這樣乙個序列,
演算法一在計算連續子串行和的時候,其過程為:
a-b、a-c、a-d、b-c、b-d、c-d
而對於演算法二,其過程為:
a-b、a-c、a-d、b-c、b-d、c-d
其過程貌似是一樣的,但是演算法一的複雜就在於沒有充分利用前面已經求出的子串行和的值。
舉個例子,演算法一在求a-d連續子串行和的值時,其過程為a-d = a-b + b-c + c-d;
而對於演算法二,a-d連續子串行和的求值過程為a-d = a-c+c-d;
這樣,演算法二充分利用了前面的計算值,這樣就大大減少了計算子串行和的步驟。
演算法三:遞迴法(分治法)
時間複雜度:o(nlogn)
易知,對於一數字序列,其最大連續子串行和對應的子串行可能出現在三個地方。或是整個出現在輸入資料的前半部(左),或是整個出現在輸入資料的後半部(右),或是跨越輸入資料的中部從而佔據左右兩半部分。前兩種情況可以通過遞迴求解,第三種情況可以通過求出前半部分的最大和(包含前半部分的最後乙個元素)以及後半部分的最大和(包含後半部分的第乙個元素)而得到,然後將這兩個和加在一起即可。
其實現**為:
int maxsubsequence(constint a,int
n)static
int maxsubsum(const
int a, int left, int
right)
center = (left + right)/2
; maxleftsum =maxsubsequence(a,left,center);
maxrightsum = maxsubsequence(a,center+1
,right);
maxleftbordersum = 0
; leftbordersum = 0
;
for(i = center;i >= left;i--)
maxrightbordersum = 0
; rightbordersum = 0
;
for(i = center+1;i <= right;i++)
return max(maxleftsum,maxrightsum,maxleftbordersum +maxrightbordersum);
} int max(int a, int b, int
c)
現在對上面的**進行相關說明:
center變數所確定的值將處理序列分割為兩部分,一部分為center前半部,一部分為center+1後半部。
在上文,我們提到,最大連續子串行的出現位置有三種情況。
對於前兩種情況,我們根據遞迴特性,可以得到:
maxleftsum =maxsubsequence(a,left,center);maxrightsum = maxsubsequence(a,center+1,right);
而對於第三種情況,我們需要先求出前半部包含最後乙個元素的最大子串行:
maxleftbordersum = 0; leftbordersum = 0
;
for(i = center;i >= left;i--)
然後,再求出後半部包含第乙個元素的最大子串行:
maxrightbordersum = 0; rightbordersum = 0
;
for(i = center+1;i <= right;i++)
最後,我們只需比較這三種情況所求出的最大連續子串行和,取最大的乙個,即可得到需要求解的答案。
return max(maxleftsum,maxrightsum,maxleftbordersum + maxrightbordersum);
我們在介紹這個演算法的開始,就已經提到了其時間複雜度,現在做乙個推導:
令t(n)是求解大小為n的最大連續子串行和問題所花費的時間。
當n==1時,t(1) = 1;
當n>1時,t(n) = t(n/2) + o(n);
有數學推導公式,我們可以得到:
t(n) = nlogn + n =o(nlogn)。
演算法四:動態規劃法
時間複雜度:o(n)
終於到了動態規劃的部分了,這麼一步一步走來,感受到了演算法的無窮魅力。那麼如何用動態規劃來處理這個問題?
首先,我們重溫將乙個問題用動態規劃方法處理的準則:
「最優子結構」、「子問題重疊」、「邊界」和「子問題獨立」。
在本問題中,我們可以將子串行與其子子串行進行問題分割。
最後得到的狀態轉移方程為:
maxsum[i] = max;
在這裡,我們不必設定陣列maxsum。
**實現:
int maxsubsequence(constint a, int
n)
return
maxsum;
}
在本**實現中,thissum持續更新,同時整個過程,只對資料進行了一次掃瞄,一旦a[i]被讀入處理,它就不再需要被記憶。(聯機演算法)
小結:
整個過程是乙個思想的選擇問題,從最初的窮舉法,到分治法,再到動態規劃法。演算法設計思想的靈活選擇是處理乙個實際問題的關鍵。
最大子列和
int maxsubsequencesum const int a,int n if thissum maxsum 如果新的子列和更大,則更新子列和 maxsum thissum return maxsum 時間複雜度o n3 int maxsubsequencesum const int a,in...
最大子列和
給出乙個長度為 n 的序列 a,選出其中連續且非空的一段使得這段和最大。第一行是乙個整數,表示序列的長度 n。第二行有 n 個整數,第 i個整數表示序列的第 i個數字 a i 輸出一行乙個整數表示答案。輸入7 2 4 3 1 2 4 3 輸出資料範圍 對於 40 的資料,保證 n leq 2 tim...
最大子列和問題
給定k個整數組成的序列,連續子列 被定義為,其中 1 i j k。最大子列和 則被定義為所有連續子列元素的和中最大者。例如給定序列,其連續子列有最大的和20。現要求你編寫程式,計算給定整數序列的最大子列和。輸入格式 輸入第1行給出正整數 k 100000 第2行給出k個整數,其間以空格分隔。輸出格式...