题目:剪绳子
给你一根长度为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。
主要思路:
思路一:动态规划:f(n) = max(f(i)*f(n-i)), 其中 0<i<n,从1到n/2遍历i,求出最大的f(n) 。为了避免重复计算,选择从下到上的顺序计算。
思路二:贪婪算法:当n大于等于5时,尽可能剪长度为3的绳子,当剩下长度为4时,把绳子剪成2段长度为2的绳子。
证明:n≥5时,2(n-2)>n, 3(n-3)>n, 并且3(n-3)≥2(n-2)
n=4时, 2×2>3×1
本题采用动态规划或者贪婪算法可以实现。一开始没有思路时,可以从简单的情况开始想,试着算以下比较短的绳子是如何剪的。
当n=1时,最大乘积只能为0;
当n=2时,最大乘积只能为1;
当n=3时,最大乘积只能为2;
当n=4时,可以分为如下几种情况:1111,121,13,2*2,最大乘积为4;
往下推时,发现n≥4时,可以把问题变成几个小问题,即:如果把长度n绳子的最大乘积记为f(n),则有:f(n)=max(f(i)*f(n-1)),0<i<n。所以思路就很容易出来了:从下往上推,先算小的问题,再算大的问题,大的问题通过寻找小问题的最优组合得到。
其实这就是动态规划法,以下是动态规划法的几个特点:
1.求一个问题的最优解
2.整体问题的最优解依赖各子问题的最优解
3.小问题之间还有相互重叠的更小的子问题
4.为了避免小问题的重复求解,采用从上往下分析和从下往上求解的方法求解问题
贪婪算法依赖于数学证明,当绳子大于5时,尽量多地剪出长度为3的绳子是最优解。
关键点:动态规划,贪婪算法
时间复杂度:动态规划:O(n^2);贪婪算法:O(1)
package jianZhiOffer;
/*
* 面试题14:剪绳子
* 题目:给你一根长度为n的绳子,请把绳子剪成m段(整数),每段绳子的长度记为k[0],k[1],...,k[m].
* 请问k[0]*k[1]*...*k[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为
* 2,3,3的三段,此时得到的最大乘积是18
*/
public class Demo14 {
//动态规划
public static int maxProductAfterCutting_solution(int length) {
if(length<2)
return 0;
if(length==2)
return 1;
if(length==3)
return 2;
int[] products = new int[length+1];
//n=2时,因为必须要减一刀,只能剪成1和1,所以最大乘积是1
//但是初始化products[2]的时候,是为了计算n>3的情况,因此如果
//有长度为2的肯定至少剪了一刀,这时候长度为2的最大乘积是2.
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 currentproduct = products[j]*products[i-j];
if(max<currentproduct)
max = currentproduct;
}
products[i] = max;
}
max = products[length];
return max;
}
//贪婪算法
public static int maxProductAfterCutting_solution2(int length) {
if(length<2)
return 0;
if(length==2)
return 1;
if(length==3)
return 2;
//当n>5的时候,尽可能多地剪去长度为3的绳子段
int timesOf3 = length/3;
//当绳子最后剩下的长度为4的时候,不能再剪去长度为3的绳子段
//此时更好的方法是把绳子剪为长度为2的两段
if(length-timesOf3*3==1)
timesOf3 -= 1;
int timesOf2 = (length - timesOf3*3)/2;
return (int) ((Math.pow(3, timesOf3))*(Math.pow(2, timesOf2)));
}
public static void main(String[] args) {
int length = 8;
int expected = 18;
int result = maxProductAfterCutting_solution(length);
System.out.println(result);
}
}