給定一整數序列A1, A2,... An (可能有負數),求A1~An的一個子序列Ai~Aj,使得Ai到Aj的和最大
例如:
整數序列-2, 11, -4, 13, -5, 2, -5, -3, 12, -9的最大子序列的和為21。
對於這個問題,最簡單也是最容易想到的那就是窮舉所有子序列的方法。利用三重循環,依次求出所有子序列的和然後取最大的那個。當然算法復雜度會達到O(n^3)。顯然這種方法不是最優的,下面給出一個算法復雜度為O(n)的線性算法實現,算法的來源於Programming Pearls一書。
在給出線性算法之前,先來看一個對窮舉算法進行優化的算法,它的算法復雜度為O(n^2)。其實這個算法只是對對窮舉算法稍微做了一些修改:其實子序列的和我們並不需要每次都重新計算一遍。假設Sum(i, j)是A[i] ... A[j]的和,那麼Sum(i, j+1) = Sum(i, j) + A[j+1]。利用這一個遞推,我們就可以得到下面這個算法:
int max_sub(int a[],int size)
{
int i,j,v,max=a[0];
for(i=0;i< FONT>
{
v=0;
for(j=i;j< FONT>
{
v=v+a[j];//Sum(i, j+1) = Sum(i, j) + A[j+1]
if(v>max)
max=v;
}
}
return max;
}
那怎樣才能達到線性復雜度呢?這裡運用動態規劃的思想。先看一下源代碼實現:
int max_sub2(int a[], int size)
{
int i,max=0,temp_sum=0;
for(i=0;i< FONT>
{
temp_sum+=a[i];
if(temp_sum>max)
max=temp_sum;
else if(temp_sum<0)
temp_sum=0;
}
return max;
}
在這一遍掃描數組當中,從左到右記錄當前子序列的和temp_sum,若這個和不斷增加,那麼最大子序列的和max也不斷增加(不斷更新max)。如果往前掃描中遇到負數,那麼當前子序列的和將會減小。此時temp_sum 將會小於max,當然max也就不更新。如果temp_sum降到0時,說明前面已經掃描的那一段就可以拋棄了,這時將temp_sum置為0。然後, temp_sum將從後面開始將這個子段進行分析,若有比當前max大的子段,繼續更新max。這樣一趟掃描結果也就出來了。