【剑指offer】12.剪绳子

题目描述

给你一根长度为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

343. 整数拆分
在这里插入图片描述

动态规划

如果面试题是求一个问题的最优解(通常是最大值或最小值),并且该问题能够分解为若干个子问题,并且子问题之间还有重叠的更小的子问题,可以考虑用动态规划求解。

在应用动态规划之前,先要考虑能否把大问题分解为小问题,分解后的小问题也存在最优解。如果把小问题的最优解组合起来能够得到整个问题的最优解,那么考虑动态规划

例如本题中,如何把长度为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(ni)) 其中 0 &lt; i &lt; n 0&lt;i&lt;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循环用来比较子问题乘积的最大值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值