剪绳子

给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1,m<=n),每段绳子的长度记为k[1],…,k[m]。请问k[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
参考链接:https://www.jianshu.com/p/65b39734430c
思路:
这种题型一般用动态规划或者贪心算法进行解决

动态规划

动态规划:需要考虑两个条件:1、该问题是否可以分解成多个小问题进行求解2、结果是否需要重复计算。
这两个条件在这里都是满足的:例如绳子的长度是 12 的时候,一刀切下去变成 2 和 10,10 就成了一个子问题。10 还需要一刀切下去变成 2 和 8,这样 2 就重复了一次。虽说把 12 切成 2 和 10 不一定是最好的方案,但是我的目的在于证明子问题是有重复情况发生的。这个时候可以把子问题得到的结果提前记录下来,从而尽量避免无意义的重复,写法也就不是函数递归了,能够获得比较好的算法。在确定可以使用动态规划的时候,我们需要明确自己的目的:我们要找到最优的子问题划分。把 12 切成 2 和 10 好呢,还是切成 3 和 9 好呢? 3 和 9 比 2 和 10 要好。但是 3 和 9 就一定是最好的吗?39<48,那么 4 和 8 就一定最好吗?你会发现,从上到下的分析,并不容易得到正确答案。那么我们应该换一种思路,从下到上的分析:

假设绳子总共有 2 厘米长,由于必须剪一刀,乘积是 1。
假设绳子总共有 3 厘米长,那么乘积是 2。
假设绳子总共有 4 厘米长,剪成 2 和 2 要比 1 和 3 强,乘积是 4。注意,我们可以只剪一刀,这样子条件已经满足了,剪出来的 3 不需要再剪一刀了,因为再剪一刀反而让乘积变小了。
假设绳子总共有 5 厘米长,剪成 2 和 3 要比 1 和 4 强。同样,剪出来的 3 和 4 不需要再剪一刀了,因为再剪一刀反而让乘积变小了。
假设绳子总共有 6 厘米长,剪成 3 和 3 要比 2 和 4 强。
分析到这里的时候,我们其实已经有一些子问题的结果了。剪出来的绳子片段:

  • 长度为 1 的时候,它最高能贡献的乘积因子是 1;
  • 长度为 2 的时候,它最高能贡献的乘积因子是 2;
  • 长度为 3的时候,它最高能贡献的乘积因子是 3,因为之前已经剪过一刀了,所以不需要把 3 再剪一次;
  • 长度为 4 的时候,它最高能贡献的乘积因子是4,这个时候多剪一刀与不剪是没区别的;
  • 长度为 5 的时候,它最高能贡献的乘积因子是 6,如果这个片段不剪的话能贡献 5,剪了的话能贡献当然选择再剪一刀;
  • 长度为 6 的时候,它最高能贡献的乘积因子是 9,不剪的话能贡献 6,剪了的话能贡献3*3=9,当然选择再剪一刀;

发现没有,上面的剩余长度为 4 的问题,可以理解成两个剩余长度为 2 的乘积;剩余长度为 5 的问题,可以理解为两个剩余长度为 2 和 3 的乘积;剩余长度为 6 的问题,可以理解为两个剩余长度为 3 的乘积……如果用函数的思想,用代表绳子剩余长度为 n 的时候能够提供的最大贡献,
在这里插入图片描述
代码如下:

#动态规划
class Solution:
    def cutRope(self, number):
        # write code here
        if number < 2:
            return 0
        if number == 2:
            return 1
        if number == 3:
            return 2      
        product = [0]*(number+1)
        product[2] = 2
        product[3] = 3 #这里的 2 指的是剩下了一段长度为 2 的绳子,可以不剪
        for i in range(4,number+1):
            max_ = 0
            for j in range(2,i/2+1):
                max_ =max(product[j]*product[i-j],max_)
            product[i] = max_
        return product[number]

贪心算法

在这里插入图片描述

#贪婪算法
class Solution:
    def cutRope(self, number):
        # write code here
        if number < 2:
            return 0
        if number == 2:
            return 1
        if number == 3:
            return 2      
        three_num = number/3
        if (number - three_num*3)==1:
            three_num -= 1 #当还剩余4时,则不考虑在剪绳子,而是直接乘4,因为3*1<4
        two_num = (number - three_num*3)/2
        return  pow(3,three_num)*pow(2,two_num)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值