這個問題對我來說還挺難的,當初做dp時水過去了,但沒徹底理解,這次打算好好分析一下,爭取徹底搞懂。
首先,像:1 -1 2 2 3 -3 4 -4 5 -5這樣的數列,想要找連續最大和,可以有很多種方法,從最慢的列舉o(n^3)到最快的動態規劃o(n),毫無疑問,我們要選擇複雜度低的演算法。所以我這裡就只分析兩種:分治法o(nlogn)和動態規劃o(n)。
分治法一般分為如下三個步驟:
劃分問題:把問題的例項劃分成子問題。
遞迴求解:遞迴解決子問題。
合併問題:合併子問題的解得到原問題的解
要求連續最大和,最大的難點在於怎麼處理負數,我們將數列拆分成兩段後,遞迴得到兩段中對應的最大值,那麼區間中對應的單獨的最大和已經求出來了,接下來要找分界點在區間中間的最大和,那麼就分別從分界點從左和右找最大和,加起來與單獨的最大和比較,這樣就可以求出總的最大和。
int maxsum(int x, int y) //區間在[x,y)
v = 0; right = a[m];
for (int i = m; i < y; i++) //從分界點右邊找連續的最大和
return max(maxs, left + right); //兩邊單獨的最大值與分界點兩邊合併後最大值比較,主要考慮負數影響
}
動態規劃的要點是用乙個陣列儲存過程中的狀態,有三種方法:
第一種比較簡單,只要簡單的按照正負思考即可,但是最後需要遍歷一遍。用乙個b[n]陣列儲存狀態,如果b[k-1]<0,那麼加乙個負數肯定會減少,所以直接從a[k]開始,b[k]=a[k];如果b[k-1]>0,那麼加乙個正數肯定變大,所以b[k]=b[k-1]+a[k]。
狀態轉移方程為b[k]=max(b[k-1]+a[k],a[k])
void dp()
printf("%d\n", max);
}
第二種方法就比較難想到了,但好處是最後不用遍歷一遍,可以考慮陣列的第乙個元素,以及最大的一段陣列(a[i], ..., a[j]),和a[0]的關係,有以下三種情況:
1. 當0 = i = j 時,元素a[0]本身構成和最大的一段,all[0]=a[0]
2. 當0 = i < j 時,和最大的一段以a[0]開始,all[0]+start[1]
3. 當0 < i 時, 元素a[0]和最大的一段沒有關係,all[0]=all[1]
從上面3中情況可以看出。可以將乙個大問題(n個元素陣列)轉化為乙個較小的問題(n-1個元素的陣列)。
假設已經知道(a[1], ...,a[n-1])中和最大的一段陣列之和為all[1],並且已經知道(a[1],...,a[n-1])中包含a[1]的和最大的一段陣列為start[1]。那麼不難看出 (a[0], ..., a[n])中問題的解all[0] = max。這樣all[0]表示的就是總的0~n的最大和。倒序dp即可。
start陣列記錄的就是包括當前值的連續最大和,比如1,-2,3,-2,start[3]就截斷從3開始。而all陣列則考慮要不要加a[k],說白了,就是三種情況:當前值不加,只有乙個當前值,當前值加上前面的包含前乙個值的最大和(要連續)。
狀態轉移方程為:all[k]=max(all[k+1],a[k],a[k]+start[k+1])
void dp()
printf("%d\n", all[0]);
}
第三種結合了前兩種的優點,不用遍歷而且便於理解:
首先需要乙個dp[k]儲存第k個數之前的最大連續和,然後還要乙個sum和temp,sum用來不斷累加a[k],如果sum>temp,就把temp更新為sum,並且如果sum<0,將sum重新賦值為0。
void dp()
}
最大連續和
求陣列中數的最大連續和,如 1,1,1,1,1 最大連續和為3 一 動態規劃 當我們從頭到尾遍歷這個陣列的時候,對於陣列裡的乙個整數,它有幾種選擇呢?它只有兩種選擇 1 加入之前的subarray 2.自己另起乙個subarray。那什麼時候會出現這兩種情況呢?設狀態為f j 表示以s j 結尾的最...
最大連續和
給出乙個長度為n的序列a1,a2,an,求最大連續和 使用列舉 時間複雜度o n 3 best a 1 初始最大值 for int i 1 i n i 設si a1 a2 ai,則ai ai 1 aj sj si 1 連續子串行的和等於兩個字首之差 時間複雜度o n 2 s 0 0 for int ...
最大連續和
思路 設sum i 為前i個元素中,包含第i個元素且和最大的連續子陣列,result 為已找到的子陣列中和最大的。對第i 1個元素有兩種選擇 做為新子陣列的第乙個元素 放入前面找到的子陣列。sum i 1 max a i 1 sum i a i 1 result max result,sum i 方...