动态规划是热门话题,如果一个面试题就问题的最优解,通常是最大值和最小值,而且能分解成若干个子问题,子问题也能分解更小的子问题,就能考虑动态规划
线分析要能否把大问题分解成小问题,小问题存在最优解,然后把小问题组合起来能够得到整个问题的最优解,用动态规划
为了避免重复求子问题,可以用从下往上的顺序计算子问题的最优解并存储下来,以此为基础球的最大问题的最优解,从上往下分析问题,从下往上求解问题
在用动态规划的时候,每一步都可能面临若干选择
动态规划四个特点:
1.求一个问题的最优解
2.整体问题的最优解依赖于各个子问题的最优解
3.大问题可以分解为若干个小问题,小问题之间还有更小的子问题
4.从上往下分析问题,从下往上求解问题
贪婪算法和动态规划算法不一样,应用贪婪算法的时候,每一步都可以做出一个贪婪选择,基于这个选择得到最优解
面试题14.剪绳子
题目:给你一根长度为n的绳子,请把绳子剪成n段(m,n都是整数,n>1,m>1),每段绳子的长度记为k[0],k[1],……k[m],请问k[0]*k[1]*……k[m]可能的最大成绩是多少?例如当绳子的长度未8,我们把他剪成长度为2,3,3的3段,此时得到的最大乘积为18
常规时间O(n^2),空间O(n)-》动态规划
时间空间都为O(1)的贪婪算法
动态规划方法
定义f(n)为把长度为n的绳子剪成若干段后各段长度乘积的最大值
在剪第一刀的时候,我们有n-1种可能,也就时间出来的第一段绳子的可能长度为1,2,……n-1。因此f(n)=max(f(i)*f(n-1)),其中0<i<n
这是一个从上到下的递归公式,会有重复子问题,大量不必要的计算,更好的办法是从下到上
当绳子长度为2的时候,只能剪成长度都为1的两段,因此f(2)等于1,当绳子长度为3的时候,可能把绳子剪成长度分别为1和2的两端或者长度都为1的三段,由于1*2>1*1*1,因此f(3)=2
public static int maxProductAfterCutting_solution1(int length){
if(length==1){
return 0;
}
else if(length==2){
return 1;
}
else 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++){
max=0;
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;
}
贪婪算法
长度剩下5以上,尽可能多剪长度为3的绳子,长度为4的时候,尽量剪长度为2的绳子
public static int maxProductAfterCutting_solution2(int length){
if(length<2){
return 0;
}
else if(length==2){
return 1;
}
else if(length==3){
return 2;
}
int timesOf3=length/3;
if(length-timesOf3*3==1){
timesOf3=timesOf3-1;
}
int timesOf2=(length-timesOf3*3)/2;
return (int)(pow(3,timesOf3))*(int)(pow(2,timesOf2));
}
在n>=5的时候,可以证明2(n-2)>n并且3(n-3)>n,当绳子长度大于等于5,我们把他剪成长度为3或者2的绳子段。另外当n>=5时,3(n-3)>=2(n-2)因此我们尽可能减去长度为3的段
当绳子长度为4的时候,剪一刀,2*2>3*1