剑指Offer 14- II. 剪绳子 II(Medium)
343. 整数拆分(Medium)
【题目连接】
题解
思路
- 数学方法【均值不等式 + 函数求极值】
- 记忆化递归
3. 动态规划
4. 动态规划空间优化
说明:
函数cuttingRope(n)的目标是返回能够获得的最大可能的差值。
函数的主要部分是一个循环,从3到n(包括n),计算并存储每个长度下的最大可能的差值。
循环中的dp[i%3]表示当绳子长度为i时的最大差值。这里使用模运算%来确保索引始终在0, 1, 2的范围内,因为当i大于3时,我们只需要考虑最近的三段绳子(即长度为i-1, i-2, i-3的绳子)。
在循环中,我们考虑三种可能的剪断位置:
在位置i-1处剪断,得到两段长度为i-1和1的绳子。差值为i-1。
在位置i-2处剪断,得到两段长度为i-2和2的绳子。差值为i-2。
在位置i-3处剪断,得到两段长度为i-3和3的绳子。差值为i-3。
我们选择上述三种可能中的最大差值作为当前长度下的最大差值。
最后,函数返回dp[n%3],即当绳子长度为n时的最大差值。
这个方法利用了动态规划的思想,通过存储并复用之前计算的结果,避免了重复计算,从而提高了效率。
代码
class Solution(object):
### 0909 记忆化递归(20 ms,12.7 MB)
def cuttingRope(self, n):
"""
:type n: int
:rtype: int
"""
def memorize(n):
## 递归终止条件
if n == 2:
return 1
## 若当前子问题已计算,则直接返回其结果
if f[n] != 0:
return f[n]
## 临时变量res用于暂存剩余的绳子(数组)
res = -1
## i表示需要剪掉多长的绳子,其长度从1到n
for i in range(1, n+1):
## 判断是否需要剪掉?
## res:表示不继续剪掉
## i*(n-i):表示n剪掉i之后,不再剪了,直接和i相乘
## i*memorize(n-i):表示n剪掉i之后,继续剪(得到子问题),计算完成后再和i相乘
res = max(res, max(i*(n-i), i*memorize(n-i)))
## 当前f数组的最后一位用于存放前面(子)问题的最大值(最优结果)
f[n] = res
return f[n]
## 初始化f数组(0),其长度为n+1,最后一位用于存放前面(子)问题的最大值(最优结果)
f = [0] * (n+1)
return memorize(n)
### 0909 动态规划(24 ms,12.9 MB)
def cuttingRope(self, n):
dp = [0] * (n+1)
dp[2] = 1
## i从3到n
for i in range(3, n+1):
for j in range(1, i):
## 判断是否需要剪掉?
dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]))
## 返回数组的最后一个元素
return dp[n]
### 0909 数学方法【均值不等式 + 求极值】(4 ms,12.8 MB)
import math
def cuttingRope(self, n):
if n <= 3:
return n - 1
a, b = n // 3, n % 3
## 若整除,则直接计算
if b == 0:
return int(math.pow(3, a))
## 若余1,则拆分一个3,将1和3转换为2*2
elif b == 1:
return int(math.pow(3, a-1)) * 2 * 2
## 若余2,则不拆分,直接计算
else:
return int(math.pow(3, a)) * 2