Skip to main content

面试题14- I. 剪绳子[medium]

面试题14- I. 剪绳子[medium]

ttps://leetcode-cn.com/problems/jian-sheng-zi-lcof

给你一根长度为 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。

示例 1:

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

示例 2:

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

提示:

2 <= n <= 58

注意:本题与主站 343 题相同:https://leetcode-cn.com/problems/integer-break/

通过次数23,579 | 提交次数43,399

First Try

暴力解法竟然成功了,又是一道莫名奇妙的题目。在动态规划的题库里找到这道题,但是找不到动态规划的迁移方式,从这个角度看是失败了。

  • 从数学的角度而言,分出来的n段,肯定在最接近的时候乘积最大,但是如何证明不好说。
  • 对于切成两端的情况,假设中间值为k,其他切分可能则为k+i和k-i, 而k^2 > (k+i)*(k-i) = k^2 - i^2。 对于切分成n段的而言,则不知如何证明了,纯粹是靠朴素的数学观。
  • 遍历从2到n段的所有可能切分,得到乘积最大的即为答案。没想到竟然通过了测试。
  • 复杂度是O(n^2)左右,多项式时间。

看了题解,通过数学证明了切分后长度为e的积最大,e为浮点数,整数则选择3,然后得到O(1)的解法。但是对数学证明的过程不太有把握,先不管了。


class Solution(object):
def cuttingRope(self, n):
"""
:type n: int
:rtype: int
"""
if n < 2:
return n

max_rv = 1
for max_cut in range(2, n + 1):
q, r = divmod(n, max_cut)
rv = 1
for i in range(1, max_cut+1):
rv = rv * q if r < i else rv * (q + 1)
max_rv = max(max_rv, rv)
return max_rv

  • 执行用时 :24 ms, 在所有 Python 提交中击败了55.14%的用户
  • 内存消耗 :12.6 MB, 在所有 Python 提交中击败了100.00%的用户
  • failed try

想法遍历所有可能的m, 使得(n//m)^m最大,也是基于朴素的平分乘积最大的几何观,如果顺利复杂度仅有O(n).

问题是(n//m)^m最大的时候,边角余数的作用导致结果并不是最大。比如10,按刚才的算法最大的m是5 -- 2^5 = 32,但是答案却是3,虽然3^3 = 27 < 32,但是3 * 3 * 4 = 36 > 32.


class Solution(object):
def cuttingRope(self, n):
"""
:type n: int
:rtype: int
"""
if n < 2:
return n
max_cut = 0
max_sum = 0
for i in range(2, n + 1):
t = (n // i) ** i
if t > max_sum:
max_sum = t
max_cut = i
q, r = divmod(n, max_cut)
rv = 1
for i in range(1, max_cut+1):
rv = rv * q if r < i else rv * (q + 1)
print(max_cut, max_sum, q, r)
return rv
  • 数学解

一个比较容易理解的,为什么选择3的方法,比数学证明更容易信服,至少看懂了。 其他的证明里面,同样周长的拆分,也只有正方形容易理解,但是n边型之后就不好说了。

https://leetcode-cn.com/problems/integer-break/solution/shuang-jie-fa-dong-tai-gui-hua-tan-xin-fa-fu-zhao-/

image