一、需求
- 给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[1],...,k[m]。请问k[1]x...xk[m]可能的最大乘积是多少?
- 例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
二、测试样例
- 输入样例
8
- 输出样例
18
三、动态规划法
3.1 思路分析
- 定义数组dp,dp[n]表示绳子长度为n时,分割后每一段乘积的最大值;
- n == 2和n == 3时,只能切一刀,最大乘积分别为1和2;
- n == 4时,若切一刀,有两种情况:1和3及2和2,这个时候就没有必要对3再切了,也没有必要对2切了,最大乘积就是4;
- n == 5时,若切一刀,有1和4,2和3,3和2,4和1,只相当于两种情况,也没有必要再切了。
所以当n >= 4的时候,如果切下来有长度为2或3的段时,就没有必要再切了,用数组表示就是
dp[1] = 1,dp[2] = 2,dp[3] = 3,这里一定要上述步骤2区分开,这里是n >= 4时的情况,在这种情况下,对绳子切割,出现了长度为2或3的段,这时候就不用再对长度为2或3的段切割了,直接返回它们的长度即可。
3.2 代码实现
public int cutRope(int target) {
int[] dp = new int[target+1];
if(target == 2) {
return 1;
}
if(target == 3) {
return 2;
}
dp[1] = 1;
dp[2] = 2;
dp[3] = 3;
int res = 0;
for(int i = 4; i <= target; i++) {
for(int j = 1; j <= i/2; j++) {
res = Math.max(j*dp[i - j],res);
}
dp[i] = res;
}
return dp[target];
}
3.3 复杂度分析
- 时间复杂度为O(n^2);
- 空间复杂度为O(n);
四、数学推导法
4.1 思路分析
- 这里需要利用一个推论,①将绳子以相等的长度分为多段,则得到的乘积最大;
- 根据推论①可以推导出当等分的长度为 3 时可获得最大乘积,具体推导过程可见:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/mian-shi-ti-14-i-jian-sheng-zi-tan-xin-si-xiang-by/
- 在 2 的基础上,将一段绳子尽可能按照每一段为 3 的长度划分,那么这段绳子最后剩下的长度可能为0、1、2;
- 如果剩下的长度为 0,说明正好全部都是 3 等分了;如果剩下的长度为 1,这时候,我们就拿出一个 3 ,和这个 1 用 2 和 2来代替;如果剩下的长度为 2 ,那保持不变;
4.2 代码实现
class Solution {
public int cuttingRope(int n) {
if(n <= 3) return n - 1;
int a = n / 3;
int b = n % 3;
if(b == 0) return (int)Math.pow(3, a);
if(b == 1) return (int)Math.pow(3, a - 1) * 4;
return (int)Math.pow(3, a) * 2;
}
}
4.3 复杂度分析
- 时间复杂度为O(1);
- 空间复杂度为O(1);
五、贪心算法
5.1 思路分析
- 经过证明,当绳子按 3 等分时可获得最大乘积,特殊情况:①当n == 2 或 3 无法进行 3 等分,②当n == 4时,可以进行 3 等分,但是没有必要,因为最大为 4;
- 于是当绳子长度大于 5 时,我们开始进行 3 等分,经过枚举归纳,3 等分后剩余的绳子的长度为 2 , 3 , 4中的一个;
- 因此,最后的结果,只需要将等分的总长度乘以剩余绳子的长度即可;
5.2 代码实现
class Solution {
public int cuttingRope(int n) {
if(n <= 3) return n - 1;
if(n == 4) return 4;
int res = 1;
//当绳子长度大于4时,按3等分,剩下的长度只能为2,3,4,而2,3,4无须再分
while(n > 4) {
res = res * 3;
n = n - 3;
}
return res * n;
}
}
5.3 复杂度分析
- 时间复杂度为O(n);
- 空间复杂度为O(1);