面試題14:剪繩子(動態規劃,貪心演算法)
阿新 • • 發佈:2018-12-09
一、題目:
一根長度為n的繩子,剪成m段,m,n都大於1,且都為整數,每段長度記為k[0],k[1]…,k[m].求k[0]*k[1]…*k[m]可能的最大乘積
1.1解法:
兩種不同的方法解決這個問題,先用常規的需要O(n²)時間和O(n)空間的動態規劃,接著用只需要O(1)的時間和空間的貪心演算法。
二、動態規劃:
(1)是求最優解問題,如最大值,最小值;
(2)該問題能夠分解成若干個子問題,並且子問題之間有重疊的更小子問題。
2.1通常按照如下4個步驟來設計一個動態規劃演算法:
(1)刻畫一個最優解的結構特徵;
(2)遞迴地定義最優解的值;
(3)計算最優解的值,通常採用自底向上的方法;
(4)利用計算出的資訊構造一個最優解。
2.2用動態規劃分析問題
首先定義函式f(n)為把長度為n的繩子剪成若干段後各段長度乘積的最大值。在剪第一刀的時候,我們有n-1中可能的選擇,也就是剪出來的第一段繩子可能長度為1,2,…,n-1。因此f(n) = max(f(i) * f(n-i))。其中0<i<n。
2.3程式碼實現:
int maxProductAfterCutting_Solution(int length) { //繩子在3米以內,直接返回對應的值,不能用動態規劃的公式做 if(length<2) return 0; if(length==2) return 1; if(length==3) return 2; //建立陣列儲存子問題最優解 int* products=new int[length+1];//? //繩子大於3米,可以用動態規劃的公式做,f(n)=f(i)*f(n-i) //對於繩子長度為1,2,3米的,繩子的最大乘積就是長度本身 products[0]=0; products[1]=1; products[2]=2; products[3]=3; int max=0; //從4米開始計算,直到計算到總長 for(int i=4;i<=length;++i) { max=0; //對於長度為i的繩子,計算所有可能的切分,找到最大值 for(int j=1;j<=i/2;++j) { //切分組合 int product=products[j]*products[i-j]; if(max<product) max=product; products[i]=max; } } max=products[length]; delete[] products; return max; }
三、貪心演算法:
在每一步求解的步驟中,他要求每一步都是最優選擇操作,並且通過一系列的最優選擇,能夠產生一個問題的(全域性的)最優解
3.1 貪心演算法滿足的條件
1、可行的:即它必須滿足問題的約束
2、區域性最優:他是當前步驟中所有可行選擇中最佳的區域性選擇
3、不可取消:即選擇一旦選出,在演算法的後面步驟就不可更改了
3.2 用貪心演算法分析問題
如果我們按照如下的策略來剪繩子,則得到的各段繩子的長度的乘積將最大:當n≥5時,我們儘可能多的剪長度為3的繩子:當剩下的繩子長度為4時,把繩子剪成兩段長度為2的繩子。
3.3程式碼實現:
int maxProductAfterCutting_Solution2(int length) { if(length<2) return 0; if(length==2) return 1; if(length==3) return 2; //儘可能的減去長度為3的繩子段 int timesOf3=length/3; //當繩子最後剩下的長度為4的時候,不能再減去長度為3的繩子段 //此時更好的方法是把繩子剪成長度為2的兩段,因為2*2>3*1 if(length-timesOf3*3==1) timesOf3-=1; //最後小於等於4米的,儘量多的產生2米的分割 int timesOf2=(length-timesOf3*3)/2; //3的多少個三次冪乘以2的多少個2次冪 return (int)(pow(3.0,timesOf3))*(int)(pow(2.0,timesOf2)); }
四、測試及結果
4.1 測試程式碼
int main()
{
printf("%d\n",maxProductAfterCutting_Solution(10));
printf("%d\n",maxProductAfterCutting_Solution2(10));
return 0;
}