題目
給你一根長度爲 n
的繩子,請把繩子剪成整數長度的 m
段(m、n都是整數,n>1並且m>1),每段繩子的長度記爲 k[0],k[1]...k[m-1]
。請問 k[0]*k[1]*...*k[m-1]
可能的最大乘積是多少?例如,當繩子的長度是8時,我們把它剪成長度分別爲2、3、3的三段,此時得到的最大乘積是18。
示例 1:
輸入: 2
輸出: 1
解釋: 2 = 1 + 1, 1 × 1 = 1
示例 2:
輸入: 10
輸出: 36
解釋: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:
2 <= n <= 58
解題思路
解法一:動態規劃
定義函數 f(n) 爲把長度爲 n 的繩子剪成若干段後各段長度乘積的最大值,由於題目要求至少要剪一刀,假設第一刀剪在長度爲 j 的位置(1<=j<=n/2, j爲整數),那麼繩子就分成了 j 與 n-j 的兩段,那麼 f(n) = max(f(j)*f(n-j)), 其中1<=j<=n/2, j爲整數)。
初始值:
當 n=1 時,沒法減,f(1) = 0;
當 n=2 時,只能剪成 1+1 的兩段,所以 f(2) = 1*1 =1;
當 n=3 時,可以剪成 1+2 的兩段,所以 f(3) = 1*2 = 2;
當 n=4 時,可以剪成 1+3 的兩段,或者 2+2 的兩段,由於 2*2 > 1*3,所以 f(4) = 2*2 = 4;
複雜度分析:
時間複雜度:O(n^2)。
空間複雜度:O(n)。
解法二:貪心算法
歸納起來一句話,就是當 n>=5 時,應儘可能地多剪長度爲 3 的繩子。當剩下的長度爲 4 時,把繩子剪成兩段長度爲 2 的繩子。
證明:
1)當 n>=5 時,3*(n-3) > n,2*(n-2) > n,也就是說,當剩下的繩子長度大於等於5時,就把它剪成長度爲 3 或者 2 的繩子段。又當 n>=5 時,3*(n-3) >= 2*(n-2) ,所以應儘可能多剪出長度爲 3 的繩子段來。
2)當 n=4 時,如果要剪一刀,可以剪成 1+3 的兩段,或者 2+2 的兩段,由於 4 = 22 > 13,所以其實沒必要剪,只是題目要求至少要剪一刀,那就剪成 2+2 的兩段好了,最大乘積爲 2*2 = 4;
3)其他 n<4 的情況,見解法一。
複雜度分析:
時間複雜度:O(1)。
空間複雜度:O(1)。
代碼
解法一:動態規劃
class Solution {
public int cuttingRope(int n) {
// 特殊情況
if(n<2){
return 0;
}
if(n==2){
return 1;
}
if(n==3){
return 2;
}
// 其他情況
int[] dp = new int[n+1];
dp[1] = 1;
dp[2] = 2;
dp[3] = 3;
for(int i=4; i<=n; i++){
for(int j=1; j<=i/2; j++){
dp[i] = Math.max(dp[i], dp[j]*dp[i-j]);
}
}
return dp[n];
}
}
解法二:貪心算法
class Solution {
public int cuttingRope(int n) {
// 特殊情況
if(n<2){
return 0;
}
if(n==2){
return 1;
}
if(n==3){
return 2;
}
// 其他情況
// 儘可能地多剪成3
int time3 = n/3;
int nextLen = n - 3*time3;
// 如果最後剩下的長度爲1,那麼從3那裏拿出1來湊成2*2,因爲 2*2>3*1
if(nextLen == 1){
time3--;
}
int time2 = (n-3*time3)/2;
int res = (int)(Math.pow(3, time3) * Math.pow(2, time2));
return res;
}
}