1.题目
给你一根长度为n绳子,请把绳子剪成m段(m、n都是整数,n>1并且m≥1)。每段的绳子的长度记为k[0]、k[1]、……、k[m]。k[0]*k[1]*…*k[m]可能的最大乘积是多少?例如当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到最大的乘积18。
2.实现
本题采用动态规划或者贪婪算法可以实现。
2.1 动态规划实现
一开始没有思路时,可以从简单的情况开始想,试着算以下比较短的绳子是如何剪的。
- 当n=1时,最大乘积只能为0;
- 当n=2时,最大乘积只能为1;
- 当n=3时,最大乘积只能为2;
- 当n=4时,可以分为如下几种情况:1*1*1*1,1*2*1,1*3,2*2,最大乘积为4;
往下推时,发现n≥4时,可以把问题变成几个小问题,即:如果把长度n绳子的最大乘积记为f(n),则有:f(n)=max(f(i)*f(n-1)),0<i<n。所以思路就很容易出来了:从下往上推,先算小的问题,再算大的问题,大的问题通过寻找小问题的最优组合得到。
其实这就是动态规划法,以下是动态规划法的几个特点:
- 求一个问题的最优解
- 整体问题的最优解依赖各子问题的最优解
- 小问题之间还有相互重叠的更小的子问题
- 为了避免小问题的重复求解,采用从上往下分析和从下往上求解的方法求解问题
代码实现
public class Solution14 {
public static int maxResultAfterCutting(int len){
if(len<2){
return 0;
}
if(len ==2){
return 1;
}
if(len==3){
return 2;
}
//存放0-len长度的结果
int[] result=new int[len+1];
result[0]=0;
result[1]=1;
result[2]=2;
result[3]=3;
for(int i=4;i<=len;i++){
int max=0;
for(int j=1;j<i;j++){
result[i]=result[j] * result[i-j];
if(result[i]>max){
max=result[i];
}
result[i]=max;
}
}
return result[len];
}
public static void main(String[] args) {
int result=maxResultAfterCutting(5);
System.out.println(result);
}
}
2.2 贪婪算法实现
贪婪算法依赖于数学证明,当绳子大于5时,尽量多地剪出长度为3的绳子是最优解,当剩下的绳子长度为4时,把绳子剪成两段长度为2的绳子。
public class Solution14 {
public static int getResultAfterCutting(int len){
if(len<2){
return 0;
}
if(len ==2){
return 1;
}
if(len==3){
return 2;
}
//尽量切长度为3
int timesOfThree=len/3;
if(len-timesOfThree * 3 ==1){
timesOfThree--;
}
int timesOfTwo=(len- timesOfThree*3)/2;
return (int)(Math.pow(3,timesOfThree)*Math.pow(2,timesOfTwo));
}
//功能测试
@Test
public void test1(){
int result=getResultAfterCutting(8);
System.out.println(result);
}
//边界值测试
@Test
public void test2(){
int result=getResultAfterCutting(0);
System.out.println(result);
}
}