写代码的第三十五天
买股票啦啦啦啦
121. 买卖股票的最佳时机
思路
动态规划的思路那是一点也没有的。看卡哥视频有点没明白,又看了一下代码随想录网站,看文字版了解了,说的已经非常清晰了,这里我自己有几处不太好理解的地方我记录一下。
解决问题1:dp数组怎么设置什么含义?dp数组设置成二维的,我觉得一维也是能做的,但是我没写出来,设置成二维的数组看题解倒是理解,但是要是自己想肯定想不出来。dp[i][0] 表示第i天持有股票所得最多现金;dp[i][1] 表示第i天不持有股票所得最多现金。
注意1:卡哥的文字讲解版中持有的意思是这个股票有,但不一定是当前这次买的,可以是之前就买了就有。
解决问题2:递推公式是什么?递推公式中,卡哥的解析也说的很明白,分为两种情况,当前i股票持有还是不持有。情况1:第i天持有股票,在注意1中我们解释了持有不见得是当前买的,也可能是之前买的,所以第一种可能是第i-1天就持有了股票,所以和第i-1天相同为dp[i-1][0],第二种可能是第i-1天没有,当天才买,在本题中买卖只有一次,所以如果当前买入了,那么就是从手里的钱为0开始,所以为-price[i](本题要求的是最大利润,当刚买入的时候初始钱为0,所以利润 是0-price[i]),所以情况1的最大利润是dp[i][0] = max(dp[i - 1][0], -prices[i]);情况2:第i天不持有股票,也是分两种可能,第一种可能是第i-1天就不持有,所以利润和第i-1天一样,为dp[i-1][1],第二种可能是第i天卖出股票,所以为第i-1天就持有股票所得现金加上卖出的第i天的价格,为dp[i - 1][0]+prices[i],所以情况2的递推公式为dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0])
解决问题3:dp数组初始化为什么?当前第i位都是由第i-1位决定的,所以需要初始化的是dp[0][0]和dp[0][1],dp[0][0]代表持有第0天的股票的最大现金=-price[0],dp[0][1]代表不持有第0天股票的最大现金=0。
解决问题4:遍历顺序?i是由i-1位决定的,所以是从0开始遍历,从左到右。
解决问题5:输出dp数组。
正确代码:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp = [[0 for _ in range(2)] for _ in range(len(prices))]
dp[0][0] = -prices[0]
dp[0][1] = 0
for i in range(1,len(prices)):
dp[i][0] = max(dp[i - 1][0], -prices[i])
dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0])
return dp[-1][1]
122.买卖股票的最佳时机II
思路
本题和上一题的区别在于,上一题是只能进行一次买卖,本题可以多次买卖,但是每次买入之前都要把之前的卖掉!!!利润是求的总和利润
所以区别就在于递推公式这里,还是分两种情况,dp[i][0] 表示第i天持有股票所得最多现金;dp[i][1] 表示第i天不持有股票所得最多现金。
情况1:第i天持有股票,第一种可能是第i-1天就持有了股票,所以和第i-1天相同为dp[i-1][0],第二种可能是第i-1天没有,当天才买,在本题中买卖多次,所以如果当前买入了,那么就是把前面的股票抛售掉了,所以抛售的利润为第i-1天不持有股票所得的最多现金dp[i-1][1]开始再减去当前买股票的钱price[i],所以情况1的最大利润是dp[i][0] = max(dp[i - 1][0], dp[i-1][1]-prices[i]);
情况2:第i天不持有股票,也是分两种可能,第一种可能是第i-1天就不持有,所以利润和第i-1天一样,为dp[i-1][1],第二种可能是第i天卖出股票,所以为第i-1天就持有股票所得现金加上卖出的第i天的价格,为dp[i - 1][0]+prices[i],所以情况2的递推公式为dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0])
正确代码:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp = [[0 for _ in range(2)] for _ in range(len(prices))]
dp[0][0] = -prices[0]
dp[0][1] = 0
for i in range(1,len(prices)):
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
思路
本题可以进行两次买卖。
解决问题1:dp数组的含义是什么?dp[i][0]没有操作;dp[i][1]第一次持有股票;dp[i][2]第一次不持有股票;dp[i][3]第二次持有股票;dp[i][4]第二次不持有股票。
解决问题2:递推公式是什么?dp[i][0]不定义;
dp[i][1]分两种,第一种是第i-1次已经持股dp[i][1]=dp[i-1][1];第二种是第i-1次没持股,第i次才持股dp[i][1]=dp[i-1]0-prices[i],一定是选最大的,所以 max(dp[i-1][1], dp[i-1][0] - prices[i]);
dp[i][2]分两种,第一种是i-1的时候就不持有股票了dp[i-1][2];第二种是i-1时持有,第i次才卖掉所以dp[i][2] = dp[i-1][1]+ prices[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] = max(dp[i-1][4], dp[i-1][3] + prices[i]);
解决问题3:初始化为什么?dp[0][0] = 0;dp[0][1] = -prices[0];这两个和之前是一样的;dp[0][2]当天买入当天卖出所以=0;当前一次买入卖出结束后才有第二次的买入,可以看作是第一次都是当天买当天卖,那么第一次的利润就是0,所以dp[0][3] = -prices[0];同理dp[0][4] = 0;
解决问题4:遍历顺序?从前向后。
正确代码:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp = [[0 for _ in range(5)] for _ in range(len(prices))]
dp[0][1] = -prices[0]
dp[0][3] = -prices[0]
for i in range(1,len(prices)):
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][-1]