剑指 Offer 14- I. 剪绳子
思路
考虑多种解法
- Memoization. 记忆化。也叫从顶到底的算法。将一个列表初始化成0(或者-1在某些题中),如果不是初始化的值就直接读取,如果是的话就迭代的找符合要求的值存入。很好理解。时间复杂度 O ( 2 N ) O(2^N) O(2N),空间复杂度 O ( N ) O(N) O(N)
- DP,从底向顶。根据初始值,和剪下来j后不剪,和剪下来j后继续剪三种情况取最大来从底到顶地写入。时间复杂度 O ( N 2 ) O(N^2) O(N2), 空间复杂度 O ( N ) O(N) O(N)
- DP优化解法。用%3的余数作为保存的空间,这样只需要一次遍历就可得到最大值。时间复杂度 O ( N ) O(N) O(N),空间复杂度 O ( 1 ) O(1) O(1)
- 根据一点先验知识,为尽量多切,且切3的段数越多,最后结果越大。直接数学公式解决。
- 需要mol 1 e 9 + 7 1e^9+7 1e9+7(1000000007) 8个0
代码
class Solution:
def cuttingRope(self, n: int) -> int:
# 使用辅助函数
def memoize(n):
if n == 2: return 1
if f[n] != 0: # 如果f[n]已经计算过,直接返回避免重复计算
return f[n]
res = -1
for i in range(1, n):
res = max(res, max(i * (n - i),i * memoize(n - i)))
f[n] = res
return res
f = [0 for _ in range(n + 1)]
return memoize(n)
DP:
class Solution:
def cuttingRope(self, n: int) -> int:
dp = [0 for _ in range(n+1)]
dp[2] = 1
for i in range(3, n+1):
for j in range(i):
# 分别意味着,初始,剪下来j后不剪,剪下来j后继续剪
dp[i] = max(dp[i], max((i-j)*j, dp[i-j]*j))
# print (dp)
return dp[n]
DP优化:
class Solution:
def cuttingRope(self, n: int) -> int:
dp = [0, 1, 1]
for i in range(3, n+1):
dp[i%3] = max(
1* max(dp[(i-1)%3], i-1),
2* max(dp[(i-2)%3], i-2),
3* max(dp[(i-3)%3], i-3))
return dp[n%3]
数学公式
class Solution:
def cuttingRope(self, n: int) -> int:
if n<= 3: return n-1
a, b = n%3, n//3
if a==0: return int(math.pow(3, b))
if a==1: return int(math.pow(3, b-1)*4)
else: return int(math.pow(3, b)*2)
收获
这道题需要学到的除了动态规划的思想,还有多种解题方式,以及根据提示一点点探索时间空间最优解法的。
剑指 Offer 42. 连续子数组的最大和
思路
- 用dp来做,dp用来存储以当前位置为尾的子数组的最大值。
- 改进:因为dp[i]只与dp[i-1]和num[i]有关,可以原地替换来节约空间。
代码
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
dp = ['/' for _ in range(len(nums))]
for i in range(len(nums)):
if dp[i-1] == '/' or dp[i-1] <0 : dp[i] = nums[i]
else:
dp[i] = dp[i-1]+nums[i]
return max(dp)
改进:
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
for i in range(1, len(nums)):
nums[i] += max(nums[i-1], 0)
return max(nums)
分析
空间复杂度 O ( 1 ) O(1) O(1)