概述
动态规划的一种典型题型:
一般的动态规划都是
dp[n] = f(dp[x])
但是有一种题型的动态规划的转移方程是:
dp[n] = f(g[x])
g[x] = dp[x]
这种就是含状态机的动态规划问题
股票交易问题的通用解法:
-
定义动态数组:
dp[i][k][0] 代表当前天是第i天,已经交易k次,现在不持有股票的最大利润
dp[i][k][1] 代表当前天是第i天,已经交易k次,现在持有股票的最大例如 -
初始化:
初始化的方向主要有两个原则:没有交易的时候利润为0,不可能事件的利润为 -inf
第0天,如果还没持有股票,利润为0
第0天,已经持有股票,利润为-price[0] -
转移方程
# 卖出不消耗次数 # 第i天,已经进行k次交易,并且不持有股票,我的值要不然就是前一天已经进行k次交易且不持有股票。要不就是前一天只进行k次交易并且持有股票,然后今天我把股票卖掉 dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]) # 买入消耗次数 # 第i天,已经进行k次交易,并且持有股票,我的值要不然就是前一天已经进行k次交易且持有股票。要不就是前一天只进行k-1次交易并且不持有股票,然后今天我把股票买回来 dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])
-
循环区间
for i in range(size): for k in range(max_K,0,-1): #k为什是倒叙呢,不理解?[max_k,1] pass
-
返回结果
最后一天,交易了max_k次,并且不持有股票的利润dp[-1][max_k][0]
题型:k为有限次
k为1次:121. 买卖股票的最佳时机
class Solution:
def maxProfit(self, prices) -> int:
max_K = 1
size = len(prices)
if size == 0:
return 0
# dp[i][k][j] i [0,size-1] k [0,max_K] j [0,1]
dp = [[[0, 1] for _ in range(max_K + 1)] for _ in range(size)]
for i in range(size):
for k in range(max_K,0,-1):
if i == 0: # 初始化
dp[i][k][0] = 0
dp[i][k][1] = -prices[i]
else:
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])
return dp[size - 1][max_K][0]
k为2次:123. 买卖股票的最佳时机 III
class Solution:
def maxProfit(semaxlf, prices: List[int]) -> int:
max_k = 2
size = len(prices)
dp = [[[0,0] for _ in range(max_k + 1)] for i in range(size)]
if size == 0:
return 0
for i in range(size):
for k in range(max_k, 0, -1):
if i == 0:
dp[i][k][0] = 0
dp[i][k][1] = -prices[i]
else:
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])
return dp[size - 1][max_k][0]
k次交易:188. 买卖股票的最佳时机 IV
class Solution:
def maxProfit(self, max_k: int, prices: List[int]) -> int:
size = len(prices)
if size == 0:
return 0
dp = [[[0,0] for i in range(max_k + 1)] for i in range(size)]
for i in range(size):
for k in range(max_k, 0, -1):
if i == 0:
dp[i][k][0] = 0
dp[i][k][1] = -prices[i]
else:
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])
return dp[-1][max_k][0]
题型:k为无限次
当k为无限次的时候,k就不再有参考意义,k将退化成二维
-
定义dp
dp[i][0]
# 代表第i天不持有股票
dp[i][1]
# 代表第i天持有股票 -
初始化
当i为0时:
dp[i][0 = 0 dp[i][1] = -price[0]
-
转移方程
dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i]) dp[i][1] = max(dp[i-1][1],dp[i-1][0] - prices[i])
k为无限次:122. 买卖股票的最佳时机 II
class Solution:
def maxProfit(self, prices: List[int]) -> int:
size = len(prices)
if size == 0:
return 0
dp = [[0,0] for _ in range(size)]
for i in range(size):
if i == 0:
dp[i][0] = 0
dp[i][1] = -prices[i]
else:
dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i])
dp[i][1] = max(dp[i-1][1],dp[i-1][0] - prices[i])
return dp[-1][0]
k为无限次带手续费:714. 买卖股票的最佳时机含手续费
根据示例可知,只是卖出的时候收手续费
class Solution:
def maxProfit(self, prices,fee) -> int:
size = len(prices)
if size == 0:
return 0
dp = [[0,0] for _ in range(size)]
for i in range(size):
if i == 0:
dp[i][0] = 0
dp[i][1] = -prices[i]
else:
dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i]-fee) # 卖出的时候交手续费
dp[i][1] = max(dp[i-1][1],dp[i-1][0] - prices[i])
return dp[-1][0]
k为无限次,带冷冻期:309. 最佳买卖股票时机含冷冻期
class Solution:
def maxProfit(self, prices: List[int]) -> int:
size = len(prices)
if size == 0:
return 0
dp = [[0,0] for _ in range(size)]
for i in range(size):
if i == 0:
dp[i][0] = 0
dp[i][1] = -prices[i]
elif i == 1:
dp[i][0] = max(0, prices[i]-prices[i-1]) #如果第二天没有股票,要不第一天就没有,要不就是第一天买了,第二天卖出
dp[i][1] = max(-prices[i-1], -prices[i]) # 我在第二天拥有股票,要不就是第一天买入,要不是第二天买入
else:
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
dp[i][1] = max(dp[i - 1][1], dp[i - 2][0] - prices[i])
return dp[size - 1][0]