給定一段鋼條,和不同長度的價格,問如何切割使得總價格最大。
示例:
長度爲1的最大收益爲:1
長度爲2的最大收益爲:5
長度爲3的最大收益爲:8
長度爲4的最大收益爲:10
長度爲5的最大收益爲:13
長度爲6的最大收益爲:17
長度爲7的最大收益爲:18
長度爲8的最大收益爲:22
長度爲9的最大收益爲:25
長度爲10的最大收益爲:30
遞歸1
切成兩段各長2英寸的鋼條,將產生P2 + P2 = 5 + 5 = 10 的收益,爲最優解。
長度爲n英寸的鋼條共有2^(n-1)種不同切割方案,因爲在距離鋼條左端 i (i=1, 2, … , n-1)英寸處,總是可以選擇切割或者不切割。用普通的加法符號表示切割方案,因此7=2+2+3表示將長度爲7的鋼條切割爲3段:2英寸,2英寸,3英寸。
若一個最優解將鋼條切割爲k段(1≤k≤n),那麼最優切割方案 n = i1 + i2 + … + ik.
將鋼條切割爲長度分別爲i1, i2, … , ik的小段,得到的最大收益爲 Rn = Pi1 + Pi2+…+Pik
對於上面表格的價格樣例,可以觀察所有最優收益值Ri (i: 1~10)以及最優方案:
長度爲1:切割方案1=1(無切割)。最大收益R1 = 1
長度爲2:切割方案2=2(收益5),1+1=2(收益2)。最大收益R2 = 5
長度爲3:切割方案3=3(收益8),1+2=3(收益6),2+1=3(收益6)。最大收益8
長度爲4:切割方案4=4(收益9),1+3=4(收益9),2+2=4(收益10),3+1=4(收益9),1+1+2=4(收益7),1+2+1=4(收益7),2+1+1=4(收益7),1+1+1+1=4(收益4)。最大收益10
長度爲5:切割方案5=5(10),1+4=5(10),2+3=5(13),1+1+3=5(10),2+2+1=5(11),1+1+1+1+1=5(5),其他是前面的排列。最大收益13
依次求出。。。
更一般的,對於Rn(n≥1),可以用更短的鋼條的最優切割收益來描述它:
Rn = max(Pn, R1+Rn-1, R2 + Rn-2, … , Rn-1 + R1)
第一個參數Pn對應不切割,直接出售長度爲n的方案。
其他n-1個參數對應n-1種方案。對每個i=1,2,….,n-1,將鋼條切割爲長度爲i和n-i的兩段,接着求解這兩段的最優切割收益Ri和Rn-i;(每種方案的最優收益爲兩段的最優收益之和)。
由於無法預知哪種方案會獲得最優收益,必須考察所有可能的 i ,選取其中收益最大者。若不切割時收益最大,當然選擇不切割。
注意到:
- 爲了求解規模爲n的原問題,先求解子問題(子問題形式完全一樣,但規模更小)。
- 即首次完成切割後,將兩段鋼條看成兩個獨立的鋼條切割問題實例。
- 通過組合兩個相關子問題的最優解,並在所有可能的兩段切割方案中獲取收益最大者,構成原問題的最優解。
稱鋼條切割問題滿足最優子結構性質:
問題的最優解由相關子問題的最優解組合而成,而這些子問題可以獨立求解。
除上述解法,問題可化簡爲一種相似的遞歸:從左邊切割下長度爲 i 的一段,只對右邊剩下的長度爲 n-i 的一段進行繼續切割(遞歸求解),對左邊一段則不再進行切割。
即問題分解的方式爲:將長度爲n的鋼條分解爲左邊開始一段,以及剩餘部分繼續分解的結果。(這樣,不做任何切割的方案可以描述爲:第一段長度爲n,收益爲Pn,剩餘部分長度爲0,對應收益爲R0 = 0)。於是得到上面公式的簡化版本:
在此公式中,原問題的最優解只包含一個相關子問題(右端剩餘部分的解),而不是兩個。
public static int Violence (int n, int[] price){
if (price == null || price.length == 0 || n <= 0){
return 0;
}
int max = 0;
// 題目轉化爲: 到索引n處之前各個數組之和的最大值
// i = 0,表示不切割,假設不切割時利潤最大
// i = 1時,表示當切割1
for (int i = 0; i <= n; i++){
return Math.max(price[n], price[1] + price[n-1]);
}
return max;
}
自頂而下備忘
- 用一個map將