【面试题14 剪绳子】
难度: 中等
限制: 2 <= n <= 58
给你一根长度为 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。
Leetcode题目对应位置: 面试题14:剪绳子
典型的动态规划问题。通常考虑用动态规划解决的问题具有特点:
- 求整体问题的最优解(通常是最大/最小值)
- 整体问题能够分解成若干个子问题,且子问题之间可能还有重叠的更小的子问题
- 整体问题的最优解依赖于各个子问题的最优解
- 从上往下分析问题,从下往上求解问题,并存储子问题的最优解
思路 1:动态规划
想要得到问题最优解 f(n),比如 f(10),可以将其分解为 f(4) 和 f(6) 两个子问题,而 f(2) 又是 f(4) 和 f(6) 共同的子问题(出现了子问题重叠),所以为了避免重复求解,可以考虑从小到大的顺序计算问题的最优解并保存下来,以此为基础求取更大问题的最优解。
f
(
n
)
=
m
a
x
{
f
(
i
)
×
f
(
n
−
i
)
}
,
0
<
i
<
n
f(n) = max\{f(i) \times f(n-i)\},\ 0<i<n
f(n)=max{f(i)×f(n−i)}, 0<i<n
自下而上求解问题: 若目标是长度为 n 的绳子,那么不论将它怎么切分,都是在求解更小的子问题(<n),所以只要我们依次求得 0,1,2,… n-1 这些子问题的最优解,就能够得到 n 的最优解。
- 设一个数组
products
,用以存放长度为 i 的绳子剪成若干段(可以不剪,最大值就是其本身长度)之后各段长度乘积的最大值,索引 i 处就是长度为 i 的绳子的最优解 - 对于索引 0,1,2,3(绳子长度也是 0,1,2,3)比较特殊,0 和 1 无法切分,最大值都是 0;2 只有一种切法(1+1),其最大值就是 1;3 有两种切法(1+2 和 1+1+1),其最大值就是2。注意将这前 4 种特殊情况和 products 数组区分开
优化: 分析绳子的切法,发现一个长度为 n 的绳子切一刀,前一段儿长 i,后一段儿长 n-i,其实与前一段儿长 n-i,后一段儿长 i 是完全等效的,用实际的数字来看:
n=2 1 n=3 1
n=4 2 n=5 2
n=6 3 n=7 3 ...
n 为绳子长度,后面对应的是切法。对于 n=3 来说,第一次切成 1+2 和 2+1 是等效的,所以可以看作只有 1 种切法。因此对于一个长度为 n 的绳子,其切法只有 n // 2
种(// 表示向下取余)。
时间复杂度: O(n^2)
空间复杂度: O(n)
class Solution:
def cuttingRope(self, n: int) -> int:
if n < 2: return 0
if n == 2 : return 1
if n == 3: return 2
products = [0] * (n+1)
products[0], products[1], products[2], products[3] = 0, 1, 2, 3
# 计算长度为i的绳子的最优解
for i in range(4, n+1):
max_ = 0
for j in range(1, i//2+1):
pro = products[j] * products[i - j]
if max_ < pro:
max_ = pro
products[i] = max_
return products[n]
思路 2:贪婪算法
按照如下策略剪绳子,得到的各段绳子长度乘积最大:
- 当 n >= 5 时,尽可能地多剪长度为 3 的绳子
- 当最后剩下的绳子长度为 4 时,将绳子剪成两段长度为 2 的绳子,而不是 3 和 1
时间复杂度: O(1)
空间复杂度: O(1)
class Solution:
def cuttingRope(self, n: int) -> int:
if n < 2: return 0
if n == 2 : return 1
if n == 3: return 2
timesOf3 = n // 3
if n - timesOf3 * 3 == 1:
timesOf3 -= 1
timesOf2 = (n - timesOf3 * 3) / 2
return int(pow(3, timesOf3) * pow(2, timesOf2))
证明:
- 当 n>= 5 时,2(n-2) > n,3(n-3) > n 且 3(n-3) > 2(n-2),因此应该尽可能地多剪长度为 3 的绳子段。
- 当 n = 4 时,2 + 2 要优于 1+3。
参考资料:
[1] 剑指 offer 第二版
[2] LeetCode 刷题 面试题14:剪绳子