面试题14:剪绳子
题目要求:
给你一根长度为n的绳子,请把绳子剪成m段,记每段绳子长度为k[0],k[1]…k[m-1],求k[0]k[1]…k[m-1]的最大值。已知绳子长度n为整数,m>1(至少要剪一刀,不能不剪),k[0],k[1]…k[m-1]均要求为整数。
例如,绳子长度为8时,把它剪成3-3-2,得到最大乘积18;绳子长度为3时,把它剪成2-1,得到最大乘积2。
解题思路:
本题有动态规划算法的几个明显特征:
(1)是求最优解问题,如最大值,最小值;
(2)该问题能够分解成若干个子问题,并且子问题之间有重叠的更小子问题。
通常按照如下4个步骤来设计一个动态规划算法:
(1)刻画一个最优解的结构特征;
(2)递归地定义最优解的值;
(3)计算最优解的值,通常采用自底向上的方法;
(4)利用计算出的信息构造一个最优解。
以此题为例,我们定义长度为n的绳子剪切后的最大乘积为f(n),剪了一刀后,f(n)=max(f(i)*f(n-i));假设n为10,第一刀之后分为了4-6,而6也可能再分成2-4(6的最大是3-3,但过程中还是要比较2-4这种情况的),而上一步4-6中也需要求长度为4的问题的最大值,可见,各个子问题之间是有重叠的,所以可以先计算小问题,存储下每个小问题的结果,逐步往上,求得大问题的最优解。
package chapter2;
/**
* Created by ryder on 2017/7/5.
* 剪绳子
*/
public class P96_CuttingRope {
public static int maxCutting(int length){
if(length<2) return 0;
if(length==2)return 1;
if(length==3)return 2;
int[] dp = new int[length+1];
dp[0]=0;
dp[1]=1;
dp[2]=2;
dp[3]=3;
int max = 0;
int temp = 0;
for(int i=4;i<=length;i++){
max = 0;
for(int j=1;j<=i/2;j++){
temp = dp[j]*dp[i-j];
if(temp>max)
max = temp;
}
dp[i] = max;
}
return dp[length];
}
public static void main(String[] args){
for(int i=2;i<10;i++){
System.out.println("长度为"+i+"的最大值->"+maxCutting(i));
}
}
}
运行结果
长度为2的最大值->1
长度为3的最大值->2
长度为4的最大值->4
长度为5的最大值->6
长度为6的最大值->9
长度为7的最大值->12
长度为8的最大值->18
长度为9的最大值->27
上述算法的时间复杂度为o(n^2);但其实,可以使用贪婪算法在o(1)时间内得到答案:n<5时,和动态规划一样特殊处理;n>=5时,尽可能多地剪长度为3的绳子,当剩下的绳子长度为4时,剪成2-2;比如长度为13的绳子, 剪成3-3-3-2-2;贪婪算法虽然快,但一般都思路奇特,可遇不可求。且面试官一般都会要求证明,数学功底要好 。