题目
给你一根长度为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。
思路
1.动态规划方法
四个特点:
①求一个问题的最优解;
②整体的问题的最优解是依赖于各个子问题的最优解;
③小问题之间还有相互重叠的更小的子问题;
④从上往下分析问题,从下往上求解问题;
这其实有点像斐波那契数列的思路,就是把绳子分成若干段,再把若干段分成小段,一直分到最长度为2即f(2)的时候,因为很容易知道f(1),f(2),f(3),我们就能得到f(4),f(5)这样从下向上再计算问题,最终求得解。
2.贪婪算法
当n>=5时,尽可能多地剪长度为3的绳子;当剩下绳子长度为4时,把绳子剪成两段。
ps:为什么尽可能剪成长度为3呢,因为当n>=5时,3(n-3)>=2(n-2)==> n>=5,
为什么不是4或者5或者更多呢,是因为可以把5又分成2和3,4又分成了2和2,所以是3。
复杂度分析
动态规划时间复杂度是O(n^2),空间复杂度O(n);
贪婪算法时间空间复杂度都是O(1).
代码
package Offer;
public class T14 {
//剪绳子问题-动态规划
public static int solution(int length){
if(length < 2)
return 0;
if(length == 2)
return 1;
if(length == 3)
return 2;
int products[] = new int[length+1];
products[0] = 0;
products[1] = 1;
products[2] = 2;
products[3] = 3;
int max = 0;
for(int i = 4; i <=length; 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];
return max;
}
//剪绳子-贪婪算法
public static int solution2(int length){
if(length < 2)
return 0;
if(length == 2)
return 1;
if(length == 3)
return 2;
//剪长度为3的绳子段
int size = length/3;
//当剩下的长度为4时,应该剪长度为2的两段
if(length - size*3 == 1)
size -= 1;
int size0f2 = (length - size*3)/2;
return (int)(Math.pow(3,size))*(int)(Math.pow(2,size0f2));
}
public static void main(String[] args) {
long startTime1 = System.nanoTime();
int b = solution(6);
long endTime1 = System.nanoTime();
System.out.println("结果"+b+" 动态规划时间"+(endTime1-startTime1)+"ms");
long startTime=System.nanoTime(); //获取开始时间
int c = solution2(7);
long endTime=System.nanoTime(); //获取结束时间
System.out.println("结果:"+c+" 贪婪时间: "+(endTime-startTime)+"ms");
}
}