代码随想录算法训练营第四十九天| LeetCode121. 买卖股票的最佳时机、122.买卖股票的最佳时机II

一、LeetCode121. 买卖股票的最佳时机

题目链接/文章讲解/视频讲解:https://programmercarl.com/0121.%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BA.html

状态:已解决

1.思路         

        学了双指针的同学可能会下意识用双指针来做,转化成求最优间距的问题,但是这种方法太暴力了,拿去跑很容易超时。不妨再想想,我们知道某一天是否 买入/卖出/啥也不干 是跟前面几天的状态有关(比如前面买了股票后面才能卖出),因此可以往动规方向想。

(1)确定dp数组以及下标含义:

        dp数组的维度跟所需要记录的状态维数相同。将总问题拆分成子问题,也就是先求每一天的最大收益,那么天数肯定要占据一个维度;再来看第二个维度:我们知道某一天的股票动作有:买入/卖出/啥也不做 三种,那我们是不是就需要定义一个三个元素的数组呢?漏!这里我们看的是对股票的动作,实际上股票只有两种状态:第 i 天持有股票(包括第i天刚好购入),第 i 天不持有股票(包括第i天刚好售出)。因此,第二维就只用两个元素。即dp为一个二维数组,第一个维度为天数,第二个维度为是否持有股票。

注:做动规题一定要记住我们看的是状态不是动作,不要管上一天的动作,是否买卖,而只是看上一天的状态

        即:dp[i][1]:第 i 天持有股票得到的最大利润,dp[i][0]:第 i 天不持有股票得到的最大利润。

(2)确定递推公式:

① 如果第i天持有股票即dp[i][1], 那么可以由两个状态推出来

  • 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][1]
  • 第i天买入股票,所得现金就是买入今天的股票后所得现金即:-prices[i](因为只购入售出一次,因此如果第i天才购入那么前面肯定没有购入,故前面第i-1的所有所得现金都为0,减去第i天购入花的钱就为负数了)

那么dp[i][1]应该选所得现金最大的,所以dp[i][1] = max(dp[i - 1][1], -prices[i]);

② 如果第i天不持有股票即dp[i][0], 也可以由两个状态推出来

  • 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][0]
  • 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp[i - 1][1]

同样dp[i][0]取最大的,dp[i][0] = max(dp[i - 1][0], prices[i] + dp[i - 1][1]);

(3)初始化dp数组:

        根据递推公式,我们知道当前天数的两个值需要前面一天的状态值推出,因此需要初始化dp[0][0]和dp[0][1]。

        dp[0][0]代表第0天不持有股票,那么得到的最大利润为0;

        dp[0][1]代表第0天持有股票,那么得到的最大利润为-prince[0]。

      其余下标总会被覆盖,统一赋成0即可。

(4)确定遍历顺序:

        根据递推公式可以知道需要从前往后推导dp数组,所以遍历顺序就是从前往后。

(5)举例推导dp数组:

        以示例1,输入:[7,1,5,3,6,4]为例,dp数组状态如下:

        dp[5][0]就是最后的结果(因为最后一天卖出一定比不卖出赚的多)。

2.代码实现 

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(),vector<int>(2,0));
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for(int i=1;i<prices.size();i++){
            dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i]);
            dp[i][1] = max(dp[i-1][1],-prices[i]);
        }
        return dp[prices.size()-1][0];
    }
};

时间复杂度:O(n)

空间复杂度:O(n)

二、122.买卖股票的最佳时机II

题目链接/文章讲解/视频讲解:https://programmercarl.com/0122.%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BAII%EF%BC%88%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%EF%BC%89.html

状态:已解决

1.思路 

        这题对比上面121题的不同就在于 “同一股票可交易多次” 的条件。在上题的分析中,只有在第i天持有股票的情况下我们使用了相应的条件。

        上题中,我们说因为一个股票只能交易一次,因此在dp[i][1]的第二种来源:第i天刚好购入时,前面由于没有购入过股票,故dp[i-1][0]得到的总金额为0,减去第i天买股票的钱能得到的总金额为负数0-price[i];

        而此题中,由于同一支股票可以反复交易,那么在第i天刚好购入时,前面可能经历了很多次购入售出了,故dp[i-1][0]是有可能有金额累积的,因此第i天买股票能得到的总金额为:dp[i-1][0]-price[i]。

        两道题只有这个递推式稍微不同,其余都一致。

2.代码实现

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(),vector<int>(2,0));
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for(int i=1;i<prices.size();i++){
            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[prices.size()-1][0];
    }
};

时间复杂度:O(n)

空间复杂度:O(n)

  • 20
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值