剑指 Offer 14- I—剪绳子

题目描述

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

题目直达

力扣https://leetcode-cn.com/problems/jian-sheng-zi-lcof/

解法1—数学思维

我们先考虑子绳子的长度,就是剪完之后,每一个子段的长度。核心思路是:尽可能把绳子分成 长度为3的小段,这样乘积最大。

证明如下:

首先回顾我们学过的数学知识,有一个基本不等式:

假设我们把 x1,x2,......xm  看成绳子的每一段,那么 x1+x2+....+xm = n

所以我们可以得到:

当且仅当 x1=x2=...=xm 时,“=”成立。

所以说它的最大值是在我们尽可能的将绳子平均分的情况下取到的,假设我们把长度为n的绳子平均分成m份,每份的长度就为 x = (n/m)  —> m=(n/x) 。这个时候,我们所要求的乘积 f(x)为:

我们想要求f(x)的极值,所以我们想到了求导。

最接近e的正整数的是2和3。

所以我们希望尽可能的将绳子按照 3 这个长度来划分。

C++代码实现

class Solution {
public:
    int cuttingRope(int n) {
        if(n<=3) //n=2和3的情况
            return n-1;
        int res=1;
        if(n%3==1) //n>=4
        {
            
            res*=4;
            n-=4;
        }
        else if(n%3==2)
        {
            res*=2;
            n-=2;
        }
        // n%3==0,这时n就是3的倍数了,我们就每次剪三、剪三........
        while(n)
        {
            res*=3;
            n-=3;
        }
        return res;

    }
};

 解法2—动态规划

我们先定义 F(n) 为长度为n的绳子至少剪一刀的最大的乘积,即输入为n的时候,最优解的答案。

进一步的分析,如果长度为n的绳子,如果我们要剪一刀的话,一共有n-1种减法,也就是剪下来的长度分别为:1、2、3...n-1。

假设剪下来的长度为i,则剩下来 n-i 长度的绳子。对于剩下来 n-i 的绳子来说,我们可以剪,也可以不剪。如果不剪,则F(n)=i*(n-i);如果剪,则F(n)=i*F(n-i)。

因此,当 i 固定时,有 F(n) = max( i*(n-i),i*F(n-i) )。由于长度为n的绳子,我们有 n-1种剪发,因此i的取值范围是 1~n-1。我们要遍历所有的 i ,得到F(n),并且对于不同的i,我们要保持 F(n)的最大化。即在i的循环中,我们要让其 F(n) = max{ F(n), i*(n-i), i*F((n-i)) }。

所以:F(n)=max[i * max(n-i,F(n-i)) ,F(n)]。为在i =1,2,3,....n-1 的情况下,所得到的最大值。

初始化:0 不是正整数,1 是最小的正整数,0 和 1 都不能拆分,因此 F(0)=F(1)=0。

返回值:最终得到F(n)的值即为将长度为n的绳子拆分成至少两段绳子之后,这些绳子长度的最大乘积。

C++代码实现

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

        int* dp = new int[n+1]{0};//dp[n]为绳子长度为n时的最优解法。
        
        for(int i=2;i<=n;i++) //绳子长度范围,2到n
        {
            for(int j=1;j<i;j++) // j是剪的范围,1到 i-1
            {
                // 对于同一个i,内层循环对不同的j(即剪的长度),
                //  都还会拿到一个max,所以每次内层循环都要更新max。
                //因为j有很多个取值,所以我们要拿到在j不同取值时,保证最大的那个dp[i]
                dp[i] = max(dp[i],j*max((i-j),dp[i-j])) ;
            }
        }

        int res = dp[n];
        delete [] dp;
        return res;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心之所向便是光v

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值