121. 买卖股票的最佳时机(简单)
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
# # 1、暴力法,两次遍历:求得所有的收益值取最大值(over time)
# max_p = 0
# for i in range(len(prices)):
# for j in range(i,len(prices)):
# tmp = prices[j]-prices[i]
# max_p = max(tmp, max_p)
# return max_p
# # 2、暴力法优化:一次遍历,每个元素后面的最大值(8272ms)
# max_p = 0
# for i in range(len(prices)-1):
# tmp = max(prices[i+1:])
# max_p = max(tmp-prices[i], max_p)
# return max_p
# 3、动态规划(44ms)
# 最后的元素0表示没有持有,1表示持有;最后一天是没有持有状态
# 初始值没有持有收益为0,持有收益为负无穷表示不可能
dp_i_0, dp_i_1 = 0, float('-inf')
# 遍历每一天的情况:没有持有、持有两种可能的最大收益
for i in range(len(prices)):
# 当天没有表示前一天没有,或者当天卖出(当天利润为price[i]);
# 当天持有表示前一天就有,或者当天买入(当天利润为-price[i])
dp_i_0=max(dp_i_0, dp_i_1+prices[i])
dp_i_1=max(dp_i_1, -prices[i])
return dp_i_0
针对上题方法3,同方法(3D动态规划)类推以下问题。
(1)理解动态规划方法:
dp[i][k][0 or 1]
0 <= i <= n-1, 1 <= k <= K
n 为天数,大 K 为最多交易数
此问题共 n × K × 2 种状态,全部穷举就能搞定。for 0 <= i < n:
for 1 <= k <= K:
for s in {0, 1}:
dp[i][k][s] = max(buy, sell, rest)(2)状态转移方程
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
max( 选择 rest , 选择 sell )解释:今天我没有持有股票,有两种可能:
要么是我昨天就没有持有,然后今天选择 rest,所以我今天还是没有持有;
要么是我昨天持有股票,但是今天我 sell 了,所以我今天没有持有股票了。dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
max( 选择 rest , 选择 buy )解释:今天我持有着股票,有两种可能:
要么我昨天就持有着股票,然后今天选择 rest,所以我今天还持有着股票;
要么我昨天本没有持有,但今天我选择 buy,所以今天我就持有股票了。(3)basecase
dp[-1][k][0] = 0
解释:因为 i 是从 0 开始的,所以 i = -1 意味着还没有开始,这时候的利润当然是 0 。
dp[-1][k][1] = -infinity
解释:还没开始的时候,是不可能持有股票的,用负无穷表示这种不可能。
dp[i][0][0] = 0
解释:因为 k 是从 1 开始的,所以 k = 0 意味着根本不允许交易,这时候利润当然是 0 。
dp[i][0][1] = -infinity
解释:不允许交易的情况下,是不可能持有股票的,用负无穷表示这种不可能。(4)状态转移方程总结:
base case:
dp[-1][k][0] = dp[i][0][0] = 0
dp[-1][k][1] = dp[i][0][1] = -infinity状态转移方程:
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])第1题是k=1
122. 买卖股票的最佳时机 II(简单)k=float("inf")
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp_i_0, dp_i_1 = 0, float("-inf")
for i in range(len(prices)):
tmp = dp_i_0
dp_i_0 = max(dp_i_0, dp_i_1+prices[i])
dp_i_1 = max(dp_i_1,tmp-prices[i])
return dp_i_0
309. 最佳买卖股票时机含冷冻期(中等)k=float("-inf")+cooldown
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp_i_0, dp_i_1 = 0, float("-inf")
dp_i_pre2_0 = 0 # 冷却1天,当天的持有状态,可能为两天前的未持有状态到当天售出
for i in range(len(prices)):
tmp = dp_i_0 # 第i-1天的未持有状态
dp_i_0 = max(dp_i_0, dp_i_1+prices[i]) # 第i天的未持有状态
dp_i_1 = max(dp_i_1,dp_i_pre2_0-prices[i]) # 第i天的持有状态
dp_i_pre2_0 = tmp # 将第i-1天的未持有状态赋值给第i-2天,作为(i+1)-2天的未持有状态
return dp_i_0
714. 买卖股票的最佳时机含手续费(中等)k=float("inf") with fee
给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每次交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
dp_i_0, dp_i_1 = 0, float("-inf")
for i in range(len(prices)):
tmp = dp_i_0
dp_i_0 = max(dp_i_0, dp_i_1+prices[i]-fee)
dp_i_1 = max(dp_i_1, tmp-prices[i])
return dp_i_0
123. 买卖股票的最佳时机 III(困难)k=2
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp_i_1_0, dp_i_1_1 = 0, float("-inf")
dp_i_2_0, dp_i_2_1 = 0, float("-inf")
for i in range(len(prices)):
dp_i_2_0 = max(dp_i_2_0, dp_i_2_1 + prices[i])
# 可能是前一天没有持有,今日买入,今日的交易次数为k,则前一天的交易次数为k-1
# 统一买入时计入次数,也可卖出时计入次数
dp_i_2_1 = max(dp_i_2_1, dp_i_1_0 - prices[i])
dp_i_1_0 = max(dp_i_1_0, dp_i_1_1 + prices[i])
dp_i_1_1 = max(dp_i_1_1, dp_i_0_0 - prices[i]) # 此处dp_i_0_0=0,可以不写或循环前定义初始值
return dp_i_2_0
大牛原文参考链接:参考链接