- 题目描述
给你一根长度为 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
- 解法一:数学归纳法
分析:
假设长度为n的绳子切成a段:n=n1+n2+...+na
求解的目标是:max(n1×n2×...×na)
首先回顾下算术几何均值不等式:当且仅当
n1=n2=...=na 时等号成立
然后这里就需要大量的数学推导了.....
将绳子按照x长度分成a段的话,n=ax,则乘积为x^a,此时
x^a = x^(n/x) = (x^(1/x))^n
由于n为常数,因此 x^(1/x)为最大值时,长度的乘积最大。
接着,可以将这个问题转化为求y = x^(1/x)的极大值问题,此时可以对x求导:
另导数为0, 此时有1-lnx=0,当且仅当x=e时有极大值点。
再来,可以很容易推导出,只有将绳子等分尽量多3的长度段有最大值,但是当分成长度为3还有余数时,此时有2种情况:
余一段为2,此时则直接保留为2的长度
余一段为1,此时要考虑把拿一段长度为3的和长度为1的一起拼成两段长度为2的,此时才是最大。
时间复杂度O(1)。
class Solution: ''' 用数学归纳法求解 ''' def cuttingRope(self, n: int) -> int: max_long = 0 if n < 3: return 1 if n == 3: return 2 if n % 3 == 0: max_long = 3 ** (n//3) elif n % 3 == 2: max_long = 2 * (3 ** (n//3)) else: max_long = 2 * 2 * (3 ** ((n//3) - 1)) return max_long
- 解法二:动态规划
设 F(n) 为长度为 n 的绳子可以得到的最大乘积,对于每一个
F(n),可以得到如下分解:
此时可以把求解
F(n) 的问题分解成求解 F(n−1) 的问题
直到求解到 F(2) 时,F(2)=1,递推回去,问题就得到了解决。
这里针对这种分治的思想,需要注意:我们每次将一段绳子剪成两段时,剩下的部分可以继续剪,也可以不剪。
当将一段长度为i的绳子从j出剪下时,此时有3种情况:
1)维持原状态
2)剪下来的部分为i-j,然后就不再剪了
3)剪下来的部分是i-j,然后对i-j继续剪。
此时建立一维数组dp
- 边界条件:dp[1] = dp[2] = 1,表示长度为 2 的绳子最大乘积为 1;
- 状态转移方程:dp[i] = max(dp[i], max((i - j) * j, j * dp[i - j])),可以这样理解:
代码:
class Solution: ''' 动态规划解法 ''' def cuttingRope(self, n: int) -> int: dp = [0 for i in range(n+1)] dp[2] = 1 for i in range(3, n+1): for j in range(i): dp[i] = max(dp[i], max(j*(i - j), j * dp[i-j])) return dp[n]
时间复杂度O(n2),空间复杂度O(n).
参考题解:
本博客仅用于个人学习。
n 1 +n 2 +...+n a ≥ a n 1 n 2 ...n a
推论一: 将绳子 以相等的长度等分为多段 ,得到的乘积最大。
作者:jyd链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/mian-shi-ti-14-i-jian-sheng-zi-tan-xin-si-xiang-by/来源:力扣(LeetCode)著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。