代码随想录 Day 45 | 【第九章 动态规划part 08】121. 买卖股票的最佳时机、122.买卖股票的最佳时机II、123.买卖股票的最佳时机III
一、121. 买卖股票的最佳时机
121. 买卖股票的最佳时机视频讲解:动态规划之 LeetCode:121.买卖股票的最佳时机1_哔哩哔哩_bilibili
代码随想录
1. 解题思路
(1)定义dp数组含义:dp[i][0]表示持有这支股票得到的最大现金,dp[i][1]表示不持有这支股票得到的最大现金(注意:并不是在该天有将股票卖出的行为,而仅仅表示一个状态,有可能股票在前几天已经卖出去了)。
(2) 递推公式:
首先分析如何推出dp[i][0],dp[i][0]表示持有这支股票得到的最大现金,所以它的状态有两种,一种是前一天持有这支股票然后今天依然保持持有状态,另一种是之前一直没有购买而今天买入这只股票,所以dp[i][0] = max(dp[i-1][0], 0-prices[i]);
其次分析dp[i][1],表示不持有这支股票得到的最大现金,依旧是两种状态,一种是前一天不持有这支股票然后今天依然保持不持有状态,另一种是在第i天卖出这只股票,所以dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i])。
(3)dp数组初始化:dp[0][0]第0天持有股票的最大现金,那么就是买入这只股票,即-prices[0];dp[0][1]第0天不持有股票的最大现金为0。
(4)遍历顺序:从前向后遍历,因为依赖于前面的状态。
(5)打印dp数组。
2. 代码实现
class Solution:
def maxProfit(self, prices: List[int]) -> int:
length = len(prices)
if length == 0:
return 0
dp = [[0]*2 for _ in range(length)]
dp[0][0] = -prices[0]
dp[0][1] = 0
for i in range(1, length):
dp[i][0] = max(dp[i-1][0], -prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i])
return dp[-1][1]
二、122.买卖股票的最佳时机II
122.买卖股票的最佳时机II视频讲解:动态规划,股票问题第二弹 | LeetCode:122.买卖股票的最佳时机II_哔哩哔哩_bilibili
代码随想录
1. 与 I 的区别
本题可以买卖多次,而I只可以买卖一次,差异仅仅是在买入股票时不再是-pirces[i],初始金额并不是0-prices[i],而是dp[i-1]-prices[i]。
2. 代码实现
class Solution:
def maxProfit(self, prices: List[int]) -> int:
length = len(prices)
if length == 0:
return 0
dp = [[0]*2 for _ in range(length)]
dp[0][0] = -prices[0]
dp[0][1] = 0
for i in range(1, length):
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][1]
三、123.买卖股票的最佳时机III
这道题一下子就难度上来了,关键在于至多买卖两次,这意味着可以买卖一次,可以买卖两次,也可以不买卖。视频讲解:动态规划,股票至多买卖两次,怎么求? | LeetCode:123.买卖股票最佳时机III_哔哩哔哩_bilibili
代码随想录
1. 解题思路
本题与Ⅰ、Ⅱ的区别:至多买卖两次收获的最大利润是多少。
(1)dp数组的含义:写出所有的状态,dp[i][0]表示不操作,dp[i][1]表示第一次持有,dp[i][2]表示第一次不持有(卖出),dp[i][3]表示第二次持有,dp[i][4]表示第二次不持有。
(2)递推公式:
dp[i][0]是不操作,那么就是前一天状态推导得到的,所以dp[i][0] = dp[i-1][0];
dp[i][1]表示第一次持有,既可以延续前一天买入的状态,也可以表示第i天买入,所以dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i]);
dp[i][2]表示第一次不持有(卖出),延续前一天不持有的状态,也可以第i天卖出,所以dp[i][2] = max(dp[i-1][2], dp[i-1][1]+prices[i]);
dp[i][3]表示第二次持有,既可以是前一天持有,也可以是前一天不持有但今天买入,所以dp[i][3] = max(dp[i-1][3], dp[i-1][2]+prices[i]);
dp[i][4]表示第二次不持有,既可以延续前一天不持有,也可以第i天卖出,所以dp[i][4] = max(dp[i-1][4], dp[i-1][3]+prices[i])。
(3)dp数组初始化:
dp[0][0]不操作,那么初始化为0;dp[i][1]为-prices[0];dp[0][2]表示第一次不持有(卖出),那么也就是第0天买入并且第0天卖出,所以初始化为0;dp[0][3]=-prices[0];dp[0][4] = 0。
(4)遍历顺序:从小到大遍历。
(5)打印dp数组。
2. 代码实现
class Solution:
def maxProfit(self, prices: List[int]) -> int:
length = len(prices)
if length == 0:
return 0
dp = [[0]*5 for _ in range(length)]
dp[0][0] = 0
dp[0][1] = -prices[0]
dp[0][2] = 0
dp[0][3] = -prices[0]
dp[0][4] = 0
for i in range(1, length):
dp[i][0] = dp[i-1][0]
dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
dp[i][2] = max(dp[i-1][2], dp[i-1][1]+prices[i])
dp[i][3] = max(dp[i-1][3], dp[i-1][2]-prices[i])
dp[i][4] = max(dp[i-1][4], dp[i-1][3]+prices[i])
return dp[-1][4]