剑指offer 14 - I. 剪绳子(贪心,DP)

剑指 Offer 14- I. 剪绳子 - 力扣(LeetCode) (leetcode-cn.com)

目录

贪心

运行结果

代码

 分析

把【长度>=4】的绳子任意切成【长度>1】两段,那么这两段长度之积一定不小于原长度。

对于任意一段长度超过4的绳子,我们均可以把它切割成长度在4以内的小段来增大它各段的乘积

如果把一段绳子切割成小段,只要其中存在长度超过4的小段,那么当前的切割方式产生的乘积一定不是最大的。只有把它所有的小段的长度切割到4以内的时候,才有可能得到最大的乘积。

如果绳子的长度在5以上,切出长度为3的小段一定比长度为2的小段乘积得到的增益更大。

最终方案

动态规划

运行结果

代码


贪心

运行结果

代码

时间复杂度:O(1)

空间复杂度:O(1) 

class Solution {
public:
    int cuttingRope(int n) {
        if (n == 2) return 1;       //处理两个特殊情况
        else if (n == 3) return 2;

        int x = n / 3, r = n % 3;
        if (!r) return pow(3, x);
        else if (r & 1) return pow(3, x - 1) * 4;
        else return pow(3, x) * 2;
    }
};

 分析

 对于一整段绳子,我们可以通过切割的方式缩减它的长度,增大它的乘积

要把它切割成多少份才能得到最大的乘积呢?
我们从较小的数开始找规律
绳子长度为1,不切割
绳子长度为2,切割后可得1 * 1,结果变小了
绳子长度为3,切割后可得1 * 2或者1 * 1 * 1结果变小了
绳子长度为4,切割后可以有2*2,结果没有增大
绳子长度为5,切割后可以得到2*3 = 6 > 5结果增大了
绳子长度为6,切割一次后可以得到2*4,3*3,均大于6
绳子长度为7,切割一次后可得得到4*3,2*2*2*1,结果均大于7
……
我们从小到大开始考虑,可以逐渐发现重复子问题(动态规划解法不难理解,已经放到了最后)

我们还可以发现,把长度较长的绳子切割成小段,似乎只要每段长度>1,各段长度之积就会大于原长度。可是这里的较长具体是多长?这个发现是否正确?我们接下来就来证明这个结论。

把【长度>=4】的绳子任意切成【长度>1】两段,那么这两段长度之积一定不小于原长度。

我们把较长的一段绳子切成两段,一段长度是l,另一段长度是n-l。
根据对称性,我们不妨令n-l >= l
该结论可以表示为:
    l * (n-l) >= n
上式等价于
    n/l > l/(l-1)

左边:根据条件 n-l >= l,我们有: n / l >= 2
右边:l/(l-1) = 1 + 1/(l-1) 随着l的增大而减小,当l=2时取到最大值2
因此:
    n/l >= 2 >= l/(l-1)
所以该结论成立。并且只有在l = n/2和l=2同时满足,即n==4的情况下成立。


此结论说明:

对于任意一段长度超过4的绳子,我们均可以把它切割成长度在4以内的小段来增大它各段的乘积

也就是说

如果把一段绳子切割成小段,只要其中存在长度超过4的小段,那么当前的切割方式产生的乘积一定不是最大的。只有把它所有的小段的长度切割到4以内的时候,才有可能得到最大的乘积。

小于4的正整数只有1,2,3
显然长度是1的小段并不会增大各段的乘积,因此可供考虑的就只有2和3了。那么每小段长度是2好呢,还是3好呢?

不难发现:

当n = 5时,(n-3)*3 == (n-2)*2
当n > 5时,(n-3)*3 > (n-2)*2


因此:

如果绳子的长度在5以上,切出长度为3的小段一定比长度为2的小段乘积得到的增益更大。

如果绳子的长度为4,由于2*2 > 3*1,因此切割成2*2更划算

至此,我们的方案就可以确定了:
只要绳子长度>4,就一直切出长度为3的小段,直到长度<=4
最后剩下的长度可能为2,3,4
而又因为4 == 2 * 2,也就是说切割之后乘积不变,那我们大可不必切割,因为最终要求的只是最大的乘积,不用考虑切割次数。

因此不论最后一小段长度是多少,都可以直接乘入我们的乘积里。

由于每次均切出长度为3的小段,存在大量重复操作,那么我们不必一个一个的切割,可以借用数学方法直接一步到位。

最终方案

任意一个n都可以写成 3x + r的形式,其中x >= 0,r = 0, 1, 2
r = 0: 最后一段长度是3,乘上
r = 1: 最后一段长度不能是1,应该是4,从乘积里除掉一个3,然后乘上一个4
r = 2: 最后一段长度是2,乘上

动态规划

运行结果

代码

时间复杂度:O(n)

空间复杂度:O(n)

class Solution {
public:
    int cuttingRope(int n) {
        if (n == 2) return 1;
        if (n == 3) return 2;

        vector<int> product(n + 1);    //product[i]表示长度为i的绳子能贡献出的最大值
        product[1] = 1;
        product[2] = 2;    //不经切割,最大能贡献2
        product[3] = 3;    //不经切割,最大能贡献3
        for (int i = 4; i <= n; ++i) {
            for (int j = 1; j <= i / 2; ++j) {
                product[i] = max(product[i], product[j] * product[i - j]);
            }
        }

        return product[n];
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值