题目难度: 中等
今天继续更新剑指 offer 系列, 这道题相比上道题数据规模大了不少. 虽说还可以使用昨天的解法, 但效率比较低, 特别是数据规模大了之后会导致乘积特别大, 这也是影响效率的一个因素. 所以今天就来一种更优的解法
老样子晚上 6 点 45 分准时更新公众号 每日精选算法题, 大家记得关注哦~ 另外在公众号里回复 offer 就能看到剑指 offer 系列当前连载的所有文章了
题目描述
给你一根长度为 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。
- 2 <= n <= 1000
题目样例
示例 1
输入
2
输出
1
解释
2 = 1 + 1, 1 × 1 = 1
示例 2
输入
10
输出
36
解释
10 = 3 + 3 + 4, 3 × 3 × 4 = 36
题目思考
- 能找到什么规律吗?
- 如何运用一些数学知识?
解决方案
思路
分析
- 这次我们换个思路, 从前面几个数开始找规律:
- 首先 n<=3 时不剪最合适, 但 m>1, 所以至少得剪成 2 段, 这和昨天的分析一致, 直接返回 n-1
- n = 4: 剪成 2 段
2*2
- n = 5: 剪成 2 段
2*3
- n = 6: 剪成 2 段
3*3
- n = 7: 剪成 3 段
3*2*2
(相当于 1 个 3 和 1 个 4) - n = 8: 剪成 3 段
3*3*2
(相当于 1 个 3 和 1 个 5) - n = 9: 剪成 3 段
3*3*3
(相当于 1 个 3 和 1 个 6) - ...
- 由上可以推得:
- 我们最终只需要考虑剪成多少个 1/2/3 即可, 更大的数字总能拆分成 1/2/3 的乘积
- 3 的优先级比 2 高(参考 6 的情况, 并没有使用 3 个 2, 而是使用 2 个 3), 1 优先级最低 (因为剪一个长度为 1 的段总是意味着新的乘积会比原来的要小)
- 这个大致等价于证明
3^(n/3)
一定大于等于2^(n/2)
, 就是说相同的 n 按照 3 来分得到的乘积一定比按照 2 来分的乘积大. 这里可以用数学归纳法证得: 初始前几个 n 根据计算结果一定满足这个关系, 然后假设3^(n/3)>=2^(n/2)
, 一定会有3^((n+1)/3)>=2^((n+1)/2)
(将多的因数提出来就能够看出来了, 因为3^(1/3)>2^(1/2)
)
- 这个大致等价于证明
- 所以应该剪尽可能多的 3
n%3==0
: 直接剪成n/3
段 3 即可n%3==1
: 要把最后的1+3
变成2+2
, 这样2*2
总比1*3
大一点n%3==2
: 直接保留最后一个 2, 其他段都是 3
实现
- 根据最后分析的结论实现代码即可
复杂度
- 时间复杂度
O(1)
- 只需要几次判断
- 空间复杂度
O(1)
- 只使用了几个变量
代码
class Solution: def cuttingRope(self, n: int) -> int: MOD = 1000000007 if n <= 3: return n - 1 if n % 3 == 0: # 恰好被3整除 return 3 ** (n//3) % MOD elif n % 3 == 1: # 余数为1, 转成2*2 return 3 ** (n//3-1) * 2 * 2 % MOD else: # 余数为2, 直接乘以最后一个2 return 3 ** (n//3) * 2 % MOD
大家可以在下面这些地方找到我~😊
我的公众号: 每日精选算法题, 欢迎大家扫码关注~😊