关于贪心算法的详解,请参考详解贪心算法
What is the role of recursion in Dynamic Programming?
对于使用动态规划解决的问题有如下几种特点:
递归是函数中出现调用本身的代码,它解决的问题模型是:大的问题通过递归到小的问题最后递归到边界问题来解决。(很像 dp 解决的问题是吧)
但 dp 实则是递归的升级,它通过总结递归过程中重复计算的那些点,对结果进行缓存,最终求得最佳答案。
在一些博客中讲 dp 有两种实现方式:(个人主要认为动态规划主要是自底向上的解决方法,但此处都列出来方便大家理解)
recursion 在这个过程中发挥的作用是辅助你理解!你可以通过穷举发现有哪些步骤被重复了,然后通过从最小的子问题开始解决+缓存结果来解决问题。
F(n)= min(F(n-1),x) 此时一般通过单层循环+一维数组解决问题F(n,arr)=min(F(n-1,arr[:i])) 此时一般通过双层循环+二维数组来解决问题这个过程通常通过 recursion 来解决问题,通常我们遇到一个问题时可以用递归写出来那么就该想想是不是能用动态规划来对递归进行优化即看子问题是否有重叠或重复计算的地方。
kadane algorithm实质上是一种动态规划算法,
- 状态转移方程:F(n)=f(n)+F(n-1)>0?F(n-1):0;
- 最优子结构:F(n-1) 显然就是前面 n-1 个序列的子序列最大和,
- 但同时我们需要暂存 F(n) 的最大值
时间复杂度:O(n)
空间复杂度: O(1)
# kadane 伪代码
cur, res = 0, -sys.maxsize-1
for x in nums:
# 负数是一个关键停止点
cur = x + max(cur, 0)
# res 实际上是在暂存 cur
res = max(res, cur)
根据 kadane 算法你能写出来最小子数组和吗?
# 伪代码
cur, res = 0, sys.maxsize
for x in nums:
# 正数是一个关键停止点
cur = x + min(cur, 0)
# res 实际上是在暂存 cur
res = min(res, cur)
基于 kadane 算法的题目:
题解请参考我的 github repo
传统的股票问题我们通常使用贪心算法即选定一个贪心策略然后计算就行了,LeetCode 上也有一些股票问题的变体,它们恰恰使用动态规划更合适
class Solution:
# 根据题解,我们知道一天结束后有如下几种状态
# 未进行过任何操作;最大利润为 0
# 只进行过一次买操作;代号 buy1 = max(buy1, -prices[i])
# 进行了一次买操作和一次卖操作,即完成了一笔交易;sell1=max(sell1, buy1+prices[i])
# 在完成了一笔交易的前提下,进行了第二次买操作;buy2 =max(buy2, sell1-prices[i])
# 完成了全部两笔交易。sell2 = max(sell2, buy2+prices[i])
# 和带冷冻状态的股票交易一样的思路,都是列出多个状态进行转移,很有意思
# 时间复杂度:O(n)
# 空间复杂度:O(1)
def maxProfit(self, prices: List[int]) -> int:
buy1 = -prices[0]
sell1 = 0
buy2 = -prices[0]
sell2 = 0
for i in range(1, len(prices)):
buy1 = max(buy1, -prices[i])
sell1 = max(sell1, buy1 + prices[i])
buy2 = max(buy2, sell1 - prices[i])
sell2 = max(sell2, buy2 + prices[i])
return sell2
class Solution:
# 参考题解
# f(i) 标识第 i 天我们的最大收益,它分为如下三种情况
# f(i)(0) 当前持有一支股票
# f(i)(1) 不持有股票,且处于冷冻期
# f(i)(2) 不持有股票,不处于冷冻期
# 对应的状态转移方程如下:
# f[i][0] = max(f[i-1][2]-prices[i], f[i-1][0])
# f[i][1] = f[i-1][0]+prices[i] 即卖出了
# f[i][2] = max(f[i-1][2], f[i-1][1])
# 边界条件 f(0) = 0
def maxProfit0(self, prices: List[int]) -> int:
length = len(prices)
f = [[0, 0, 0] for i in range(length)]
f[0][0] = -prices[0]
for i in range(1, length):
f[i][0] = max(f[i - 1][2] - prices[i], f[i - 1][0])
f[i][1] = f[i - 1][0] + prices[i]
f[i][2] = max(f[i - 1][2], f[i - 1][1])
return max(f[length - 1][0], f[length - 1][1], f[length - 1][2])
# 优化空间,最终时间复杂度 O(n)
# 空间复杂度 O(1)
def maxProfit(self, prices: List[int]) -> int:
length, tmp0, tmp1, tmp2 = len(prices), -prices[0], 0, 0
for i in range(1, length):
tmp0_0, tmp1_1, tmp2_2 = tmp0, tmp1, tmp2
tmp0 = max(tmp2_2 - prices[i], tmp0_0)
tmp1 = tmp0_0 + prices[i]
tmp2 = max(tmp2_2, tmp1_1)
return max(tmp0, tmp1, tmp2)