【剑指Offer-动态规划和贪婪算法】剪绳子
问题描述
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],...,k[m]。请问k[0]xk[1]x...xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
思路1
设f(n)为将长为n的绳子剪为m段后成绩的最大值,则f(n)=max(f(i)*f(n-1)) (n>3, 0<i<n),其中的边界值为f(1)=0,f(2)=1,f(3)=2。代码如下:
class Solution {
public:
int cutRope(int number) {
if(number<2)
return 0;
if(number==2)
return 1;
if(number==3)
return 2;
int ans[number+1];
ans[1] = 0;
ans[2] = 2;
ans[3] = 3;
for(int i=4; i<=number; i++){
int max = -1;
for(int j=1; j<=i/2; j++){
if(max<ans[j]*ans[i-j])
max = ans[j]*ans[i-j];
ans[i] = max;
}
}
return ans[number];
}
};
这个要注意边界值,也就是不是所有的n对应的f(n)都满足f(n)=max(f(i)*f(n-1)),只有当n>3时才满足。
思路2
思路1的时间复杂度为O(n2),可以使用贪心算法将时间复杂度降为O(1)。
当n≥5时,有3(n-3)≥2(n-2)>n, 所以我们应该尽可能的多剪成长度为3的绳子段;当n=4时,因为2×2>3×1,所以应该剪成两段长度为2的绳子;当n<4时是边界条件,直接返回即可。代码如下:
class Solution {
public:
int cutRope(int number) {
if(number<2)
return 0;
if(number==2)
return 1;
if(number==3)
return 2;
int len3Num = number / 3;
if(number-3*len3Num==1)
len3Num--;
int len2Num = (number-3*len3Num) / 2;
return (int)(pow(3, len3Num)) * (int)(pow(2, len2Num));
}
};
总结
动态规划的4个特点:
- 动态规划用来求一个问题的最优解;
- 整体问题的最优解依赖于各个子问题的最优解;
- 可以把大问题分解成若干小问题,这些小问题之间还有相互重叠的更小的子问题;
- 从上往下分析问题,从下往上求解问题并存储。