【每日一题】Leetcode - 面试题14- I. 剪绳子 / 343. Integer Break【严谨数学证明】【时、空复杂度击败100%】

题目来源 Leetcode, 感谢🙇‍,如果内容对您有帮助,欢迎点赞。


题目描述

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

示例 1:

  • 输入: 2
  • 输出: 1
  • 解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:

  • 输入: 10
  • 输出: 36
  • 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

  • 2 <= n <= 58

解题思路

  • 在经过一番思考之后,我觉得需要解决以下两个问题:
    • 对于一段绳子如果分成 n 段,这 n 段怎么分最好?
    • 如果一个绳子可以分成 1~n 段,那么选择分成几段最好呢?
  • 对于分成 n 段怎么分的问题我们可以这么思考:
    • 对于一个绳子分成两段怎么分最好,假设分的第一段为 x x x, 那么另一段为 n − x n-x nx, 最好的分法转变为了求函数 f ( x ) = x ( n − x ) = − x 2 + n x f(x)=x(n-x)=-x^2 + nx f(x)=x(nx)=x2+nx 极值的问题,那么对于 f ( x ) f(x) f(x) 求导易得, x = 1 2 n x = \frac{1}{2} n x=21n。因此我们知道对于一段绳子而言我们最好的尽可能的均分,这样乘积最大。
    • 更加严谨的数学证明可以描述为:算术均值一定大于几何均值,可由基本不等式 a + b 2 ≥ a b \frac{a+b}{2} \geq \sqrt{ab} 2a+bab 推广有: ( a 1 + a 2 + ⋯ + a n n ) n ≥ a 1 a 2 ⋯ a n (\frac{a_1+a_2 +\cdots+a_n}{n})^n \geq a_1a_2\cdots a_n (na1+a2++an)na1a2an
    • 有了上式我们就知道如果分成n段我们该如何分:均分,有时候我们绝对均分,我们就要尽可能的均分,让所有段的方差最小,最接近均值。
  • 那接下来就需要思考分成多少段最好了?
    • 我在当时写代码的时候拍脑瓜简单一分析我觉得这个函数一定是一个先增后减函数(可举简单例子辅助思考),假定 f ( k ) f(k) f(k) 表示尽量均分k段后的乘积值,那么我们应该从 2 2 2 开始尝试直到找到这样一个 k k k,满足: f ( k − 1 ) ≤ f ( k ) ≥ f ( k + 1 ) f(k-1) \leq f(k) \geq f(k+1) f(k1)f(k)f(k+1)
    • 返回此时的 f ( k ) f(k) f(k),即为要求的最大值。

完整代码

class Solution {
public:
    int cuttingRope(int n) {
        int maxProduct = 0;
        for(int i = 2; i <= n; ++i){
            int average = n / i;
            int rem = n % i;
            int product = 1;

            for(int j = 0; j < i; ++j){
                if(rem != 0){
                    product *= (average + 1);
                    --rem;
                }else
                    product *= average;
            }

            if(maxProduct < product)
                maxProduct = product;
            else
                return maxProduct;
        }
        return maxProduct;
    }
};

执行结果
在这里插入图片描述

进一步的推导思考

如果你耐心看到这里,实在感谢!🙇‍

  • 虽然我们已经解决了问题,但是似乎还是不够完美,我们没有给出严整的数学证明过程,那么就就让我们尝试来写一写:
    ( a 1 + a 2 + ⋯ + a n n ) n ≥ a 1 a 2 ⋯ a n ⟹   m a x P r o d u c t = x n x , ( x   为 均 分 的 长 度 ) ⟹   f ( x ) = x n x = e n x l n x   的 极 大 值 为 目 标 值 ⟹   f ′ ( x ) = e n x l n x n x 2 ( 1 − l n x ) = 0 ; ⟹   x = e   是 f ( x ) ⟶ m a x ⟹   x   必 须 为 整 数 2 < e < 3 , 那 么   2   好 还 是   3   好 呢 ? ⟹   f ( 2 ) f ( 3 ) = e n l n 2 2 e n l n 3 3 = e l n 4 e l n 9 < 1 , 即 f ( 3 ) > f ( 2 ) ⟹   这 说 明 我 们 需 要 尽 可 能 多 的 选 3 , 当 截 取 完 所 有 的 3 之 后 可 能   会 剩 下   1 或 者   2   两 种 可 能 , 如 果 是   2 则 直 接 截 取 即 可 , 因 为   2 > 1 ∗ 1 ; 但 是 如 果 剩 下 的 为 1 的 话 我 们 需 要 将 已 经 拆 分 的 3 结 合 1 变 成   2 、 2 , 原 因 为   2 ∗ 2 > 3 ∗ 1 \begin{aligned} &(\frac{a_1+a_2 +\cdots+a_n}{n})^n \geq a_1a_2\cdots a_n \\ \Longrightarrow & \ maxProduct = x^\frac{n}{x},(x \ 为均分的长度)\\ \Longrightarrow &\ f(x) = x^{\frac{n}{x}} = e^{\frac n x lnx}\ 的极大值为目标值 \\ \Longrightarrow &\ f'(x) = e^{\frac n x lnx} \frac{n}{x^2}(1 - lnx) = 0; \\ \Longrightarrow &\ x = e \ 是f(x) \longrightarrow max\\ \Longrightarrow &\ x\ 必须为整数 2< e < 3, 那么\ 2\ 好还是\ 3 \ 好呢?\\ \Longrightarrow &\ \frac{f(2)}{f(3)} = \frac{e^{nln2^2}}{e^{nln3^3}} = \frac{e^{ln4}}{e^{ln9}} < 1,即f(3) > f(2)\\ \Longrightarrow &\ 这说明我们需要尽可能多的选3,当截取完所有的3之后可能\\&\ 会剩下\ 1 或者\ 2\ 两种可能,如果是\ 2 则直接截取即可,因为\\ &\ 2 > 1 * 1; 但是如果剩下的为 1 的话我们需要将已经拆分的3\\ &结合1变成\ 2、2,原因为\ 2*2 > 3 * 1 \end{aligned} (na1+a2++an)na1a2an maxProduct=xxn(x ) f(x)=xxn=exnlnx  f(x)=exnlnxx2n(1lnx)=0; x=e f(x)max x 2<e<3, 2  3  f(3)f(2)=enln33enln22=eln9eln4<1f(3)>f(2) 33  1 2  2 2>11;131 22 22>31
  • 经过上述分析我们发现我们的代码似乎可以优化,我们只需要全部取3即可,对于剩余的部分如果等于2则直接取2,如果为1则将1结合最后一个3变为2、2,共计三步操作。

优化后的代码

class Solution {
public:
    int cuttingRope(int n) {
        if(n == 2)
            return 1;
        if(n == 3)
            return 2;
            
        int maxProduct = 1;
        int count = n / 3;
       
        while(count > 0){
            maxProduct *= 3;
            --count;
        }

        if(n %3 == 2)
            maxProduct *= 2;
        else if(n % 3 == 1)
            maxProduct = (maxProduct/3*4);
            
        return maxProduct;
    }
};

执行结果
在这里插入图片描述


题目来源 Leetcode, 感谢🙇‍,如果内容对您有帮助,欢迎点赞。

### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契数列问题,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值