题目描述
给你一根长度为n的绳子,请将绳子剪为m段(m、n都是大于1的整数)。每段绳子的长度记为k[0],k[1],…k[m]。请问k[0]*k[1] *…*k[m]最大值是多少?
例如8可以剪为2、3、3的3段。 最大乘积为2 * 3 * 3 = 18
LeetCode
动态规划
如果面试题是求一个问题的最优解(通常是最大值或最小值),并且该问题能够分解为若干个子问题,并且子问题之间还有重叠的更小的子问题,可以考虑用动态规划求解。
在应用动态规划之前,先要考虑能否把大问题分解为小问题,分解后的小问题也存在最优解。如果把小问题的最优解组合起来能够得到整个问题的最优解,那么考虑动态规划
例如本题中,如何把长度为n的绳子剪为若干段,使得各段长度的乘积最大。
这个问题的目标是求解最大值,也就是最优解(这是可以应用动态规划解决的第一个特点)。
把长度为n的绳子剪为若干段得到的各段长度的乘积的最大值设为f(n),假设我们把第一刀剪在长度为i的位置(0<i<n),于是把绳子分为长度为i和n-i两段。要想得到整个问题的最优解f(n),需要同样用最优化的方法把长度为i和n-i的两段分别剪为若干段,使得他们各自检出的绳子长度乘积最大。
也就是整体问题的最优解依赖于各个子问题的最优解(这是可以应用动态规划解决的第二个特点)。
把大问题分解为若干个子问题,并且子问题之间还有重叠的更小的子问题(这是可以应用动态规划解决的第三个特点)。
以上题为例,假设绳子初始长度是10,可将绳子剪为4和6两段。也就是f(4)和f(6)都是f(10)的子问题。接下来分别求解这两个子问题。可以把长度为4的绳子分为两个长度为2的段。即f(2)是f(4)的子问题。同样我们可以把长度为6的绳子剪成长度为2和4的两段,即f(4)和f(2)都是f(6)的子问题。我们注意到f(2)是f(4)和f(6)的公共子问题。
由于子问题在分解大问题的过程中重复出现,为了避免重复求解子问题,我们可以用自下向上的顺序,先求解小问题的最优解并存储起来,再以此为基础求解大问题的最优解。
从上向下分析问题,从下向上求解问题(这是可以应用动态规划解决的第一个特点)。
我们总是解决最小问题开始,并把已经解决的子问题保存存储下来(大部分情况下保存在一维或二维数组),逐步组合起来解决大问题。
在应用动态规划的时候,我们每一步都可能面临若干个选择,在本题中,在剪第一刀的时候就有n-1种选择。由于事先不知道哪个位置是最优解,只能尝试所有的可能,然后比较出最优的解法。 f ( n ) = m a x ( f ( i ) ∗ f ( n − i ) ) f(n)=max(f(i)*f(n-i)) f(n)=max(f(i)∗f(n−i)) 其中 0 < i < n 0<i<n 0<i<n
C++
int integerBreak(int n) {
if(n<2){
return 0;
}
if(n==2){
return 1;
}
if(n==3){
return 2;
}
int products[n+1]; //多一个0 所以+1
//1到4的长度时不需要剪即为最优解
for(int i=0;i<4;i++){
products[i] = i;
}
int max = 0;
for(int i=4;i<=n;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[n];
return max;
}
在上面的代码中,子问题的最优解保存在products数组中,数组中第i个元素的值表示将长度为i的绳子剪成若干段之后各段长度乘积的最大值。
第一个for循环从下向上计算子问题最优解。第二个for循环用来比较子问题乘积的最大值。