【剑指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个特点:

  • 动态规划用来求一个问题的最优解;
  • 整体问题的最优解依赖于各个子问题的最优解;
  • 可以把大问题分解成若干小问题,这些小问题之间还有相互重叠的更小的子问题;
  • 从上往下分析问题,从下往上求解问题并存储。
posted @ 2020-03-03 22:56  Flix  阅读(203)  评论(0编辑  收藏  举报