问题:
给你一根长度为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.动态规划
求一个问题的最优解(最大值或最小值),而且该问题能够分解成若干子问题,并且子问题之间还有重叠的更小的子问题,可以考虑动态规划
(1) 分析是否能把大问题分解成小问题
(2) 分解后的每个小问题存在最优解
(3) 小问题的最优解组合起来能得到整个问题的最解
2.贪心算法求解:
每一步都可以做出一个贪婪的选择,基于这个选择,确定能够得到最优解
1. 贪心算法在对问题求解时,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解
2. 选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关
3. 题目贪心策略:当n>=5时,尽可能多地剪长度为3的绳子;当剩下的绳子长度为4时,把绳子剪成两段长度为2的绳子
1.首先用动态规划递归实现,动态规划实际上就是用空间效率换区时间效率的一种做法:
public class Test { public static int getMax(int n){ //要求长度n>1,所以这里返回0表示输入非法 if(n<2){ return 0; } //长度为2时,要求剪下段数m>1,所以最大是1x1=1 if(n==2){ return 1; } //长度为3时,要求剪下段数m>1,所以最大是1x2=2 if(n==3) { return 2; } //存储长度从 0-n 的最大结果 int[] result=new int[n+1]; result[0]=0; result[1]=1; result[2]=2; result[3]=3; //自底向上开始求解 int max=0; for(int i=4;i<=n;i++){ //每次将最大乘积清空 max=0; //因为要计算f(j)乘以f(i-j)的最大值,j超过i的一半时是重复计算,所以只需要考虑j不超过i的一半的情况 for(int j=1;j<=i/2;j++){ //计算f(j)乘以f(i-j) int tempResult=result[j]*result[i-j]; if(max<tempResult) { max = tempResult; } //更新表中的f(i)=max(f(j)·f(i-j)) result[i]=max; } } max=result[n]; return max; } }
2.采用贪心算法得到最优解:
public static int getMaxValue(int n){ if(n<2) { return 0; } if(n==2) { return 1; } if(n==3) { return 2; } //先求出长度为3的段一共有多少段 int countOfThree=n/3; //再看除了长度为3的段,还剩下多少米,如果还剩1米则表示有(n-1)个3和一个4, // 所以要把上一步算出来的3的个数减1,把最后的4剪成两段长度为2的绳子 if(n-countOfThree*3==1){ countOfThree-=1; } //当剩下的绳子长度为4时,把绳子剪成两段长度为2的绳子 int countOfTwo=(n-countOfThree*3)/2; //计算所有的3的countOfThree次方,再乘以剩下2的countOfTwo次方 return (int) ((Math.pow(3, countOfThree))*(Math.pow(2, countOfTwo))); }