zoukankan      html  css  js  c++  java
  • 343. Integer Break

    问题

    将一个正整数分成至少两个正整数的和,使得最大化这些整数的乘积,给出最大乘积。

    Input: 10
    Output: 36
    Explanation: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

    思路

    先上结论,分解的时候尽量分多一些3,分到不能分再分2,可以使得乘积最大。然后介绍以下两种理解。

    理解一
    什么时候pq > p + q,其实就是(p-1)(q-1)>1,二者等价。在正整数里,p和q都为2时pq==p+q,所以只要一个至少为2,另一个至少为3即可,也就是p+q至少为5,那么只要一个数大于等于5,就可以把这个数分解成p+q,使得pq > p + q,如果p或者q里面还有大于等于5的数,就将p或q继续分解,不断往下分解,直到因子小于5。

    因为要分解到小于5,所以最后的因子一定是2或者3(或者4),因为分成4和分成2是一样的(两个2和=一个4的乘积相同),所以我们考虑3和2要怎么分配。

    考虑一个数有m个3和n个2相加,即(x = 3m + 2n),那么积等于m个3和n个2相乘,定义一个函数,(f = 3^m * 2^n = 3^m * 2^{frac{x - 3m}{2}}),可以得到$ln f $是一个关于m递增的函数,如下公式所示。所以m越大越好,m表示3的个数,也就是说分解的时候尽量分多一些3,分到不能分再分2。

    [egin{split} ln f &= m * ln3 + frac{x - 3m}{2} * ln2 \ &= frac{x}{2} ln 2 + (ln 3 - frac{3}{2}ln 2)*m end{split}]

    理解二
    考虑均值不等式,算术平均数大于等于几何平均数,当且仅当所有数相等时取等号。

    [A_n = frac{x_1 + x_2 + ... + x_n}{n} geq sqrt[n]{x_1 cdot x_2 cdot cdot cdot x_n} = G_n ]

    也就是如果把一个数分成n个数,当均分的时候这些数的积可以取到最大。接下来就是考虑均分的时候,n是多少,或者说考虑每个数是多少?
    我们现在把要分解的数记为n,均分成n/x个x,然后写出这个积关于x的函数,(f(x) = x ^{n/x}),那么它的最大值是多少呢,先对函数求导。

    [ egin{split} ln f(x) &= (n/x) * ln x \ frac{f'(x)}{f(x)} &= frac{n}{-x^2} * ln x + frac{n}{x^2} \ f'(x) &= (1 - ln x) * n * x ^ {n/x - 2}end{split}]

    x<e时导数大于0,单调递增,x>e时导数小于0,单调递减,x=e时f(x)取最大,也就是一个数均分成多个e的时,它们的积最大。
    考虑问题为正整数,(f(2) = (sqrt{2})^n = 1.142^n < 1.442^n = (sqrt[3]{3})^n = f(3)),f(3)>f(2),所以尽量取3可以使得积最大。

    时间复杂度O(n),空间复杂度O(1)

    代码

    class Solution(object):
        def integerBreak(self, n):
            """
            :type n: int
            :rtype: int
            """
            if(n == 2 or n == 3):
                return n-1
            res = 1
            while(n>=5):
                n -= 3
                res *= 3
            res *= n
            return res     
    

    出于以下考虑,还可以这么写。其中pow的时间复杂度可以达到O(logN)。
    如果n%3 == 0,可以分解成n/3个3,它们的积为(3^{n/3})
    如果n%3 == 1,说明和的形式为n/3 * 3 + 1,把其中一个3和单独的1改写为2个2,它们的积为(3^{n/3-1} * 2 * 2)
    如果n%3 == 2,说明和的形式为n/3 * 3 + 2,它们的积为(3^{n/3} * 2)
    事实上,这三个式子综合一下就是理解一中的式子(f = 3^m * 2^n = 3^m * 2^{frac{x - 3m}{2}})

    class Solution(object):
        def integerBreak(self, n):
            """
            :type n: int
            :rtype: int
            """
            if n>=4:
                if n%3 == 0:
                    return 3**(n/3)
                if n%3 == 1:
                    return 3**(n/3-1)*4
                else:
                    return 3**(n/3)*2
            return n-1
    
  • 相关阅读:
    Java 访问权限字节码
    vscode 写js项目,自动按照eslint保存
    mac 按键符号说明
    下载git单个文件或目录
    生成密码的方式
    git 笔记
    后端数据返回的snake_case格式,但前端的规范为驼峰格式,实现一种snake_case转驼峰的方法
    wireshark 数据协议解析
    MAC App破解之路十一 charles
    生成检查的顺子的表
  • 原文地址:https://www.cnblogs.com/liaohuiqiang/p/9762585.html
Copyright © 2011-2022 走看看