劍指offer_1:給你一根長度為n的繩子,把繩子剪成m段(m、n都是整數且m > 1, n > 1),m段繩子的長度依然是整數,求m段繩子的長度乘積最大為多少? * 比如繩子長度為8,我們可以分成
<code>
package Chap2;
/**問題描述 * 給你一根長度為n的繩子,把繩子剪成m段(m、n都是整數且m > 1, n > 1),m段繩子的長度依然是整數,求m段繩子的長度乘積最大為多少? * 比如繩子長度為8,我們可以分成2段、3段、4段...8段,只有分成3段且長度分別是2、3、3時能得到最大乘積18 */
public class CutString { /** * 動態規劃法: * 動態規劃求解問題的四個特徵: ①求一個問題的最優解; ②整體的問題的最優解是依賴於各個子問題的最優解; ③小問題之間還有相互重疊的更小的子問題; ④從上往下分析問題,從下往上求解問題; * 動態規劃:
有一段長度為n的繩子,我們現在要剪第一刀,我可以選擇下第一刀的地方有1~n-1這些地方;比如長度為10的繩子,我第一刀可以在1~9這些地方下刀,共9種方式。 第一刀下去後,繩子分成兩部分,假設在i處下刀,繩子兩部分就分別為:[0~i]與[i~n],長度分為表示為i與n-i;那麼找出第一刀最合適的位置,其實就是找i在哪下刀,可以使得[0~i]與[i~n]的乘積最大,函式表示為:f(n)=max(f(i)×f(n−i))f(n)=max(f(i)×f(n−i))。 那麼如何判斷i處切最大呢?這個時候,我們就要知道,[0~i]這個長度的繩子,任意方式切,最大的乘積是多少;假如說,當我們要切一個長度為10的繩子:切成1和9與4和6,兩種方式,哪個乘積更大? 回答:不光要考慮第一刀後兩個繩子的大小,還要考慮到9、4、6這三種情況,因為第一刀切出的繩子長度是否可以再切第二刀,使它有更大的乘積,比如將9再切成 3×3×33×3×3,6切成 4×24×2,哪個更大? 這種情況下,我們可以發現,無論再怎麼切,一定是越切越短,那麼我們是否可以將小於給定長度的繩子的每一個長度的最大乘積都求出來? 即:長度為10的繩子,我們就計算出:長度1~9這9種長度的繩子,每種長度的最大乘積是多少。 要求長度9的繩子的最大乘積,我們要知道1~8各個長度的最大乘積,要知道長度8的最大乘積,就要知道1~7長度的各個最大乘積,以此類推。 * * 動態規劃版本 * f(n)定義為將長度為n的繩子分成若干段後的各段長度的最大乘積(最優解),在剪第一刀時有n-1種剪法,可選擇在0 < i < n處下刀 * 在i處下刀,分成長度為i的左半繩子和長度為n-i的右半繩子,對於這兩根繩子,定義最優解為f(i)和f(n-i),於是f(n) = max(f(i) * f(n-i)),即求出各種相乘可能中的最大值就是f(n)的最優解 * 就這樣從上到下的分下去,但是問題的解決從下到上。即先求出f(2)、f(3)的最優解,然後根據這兩個值求出f(4)、f(5)...直到f(n) * f(2) = 1,因為只能分成兩半 * f(3) = 2,因為分成兩段2*1 大於分成三段的1*1*1 * ... */ public static int maxProductAfterCutting(int length){ //長度小於2 無法分割 if(length<2) return 0; //長度等於2 一分為二 1*1 if(length==2) return 1; //長度等於3 最大為1*2=2 if(length==3) return 2; //定義一個存放>=4 長度的陣列 ,對>=4長度的最大的乘積進行臨時儲存 int[] products=new int[length+1]; //以下的前三個陣列存放的不是最大值,而是長度值 products[1]=1; products[2]=2; products[3]=3; for(int i=4;i<=length;i++) { int maxModify=0; for(int j=1;j<=i/2;j++) { int product=products[j]*products[i-j]; if(product>maxModify) maxModify=product; } //得到f(i)的最優解 products[i]=maxModify; } //返回發f(n) return products[length]; } /** * 貪婪法,不斷分出長度為3的繩子,如果最後只剩下長度為1的繩子,退一步,將得到長度為4的繩子,然後將這長度為4的繩子分成2*2(這樣分是因為2*2大於原來的3*1) * 因此n = 4時不能分出長度為3的繩子,而n = 2,n = 3可直接返回 * 當n >=5時候,滿足n >=5這個不等式的有2*(n-2) > n以及3*(n-3) > n * 注意到2+n-2 = 3+n-3 = n,也就是說分出的兩個相乘的數要滿足和為n,且同樣的n,3*(n-3)的值更大,這就是為什麼要不斷分出長度為3的繩子的原因 */ public static int maxProductAfterCutting2(int length) { // 長度為1時不滿足題意,返回0 if (length < 2) { return 0; } // f(2) if (length == 2) { return 1; } // f(3) if (length == 3) { return 2; } // 統計能分出多少段長度為3的繩子 int timesOf3 = length / 3; // 如果最有隻剩下長度為1的繩子,需要退一步,得到長度為4的繩子,重新分成2*2的 if (length - timesOf3 * 3 == 1) { timesOf3--; } // 到這步length - timesOf3 * 3的值只可能是0,2,4,所以timesOf2只可能是0, 1, 2 int timesOf2 = (length - timesOf3 * 3) / 2; return (int) Math.pow(3, timesOf3) * (int) Math.pow(2, timesOf2); } public static void main(String[] args) { System.out.println(maxProductAfterCutting(8)); System.out.println(maxProductAfterCutting2(8)); } } </code>