代码随想录算法训练营第35天|121. 买卖股票的最佳时机、122.买卖股票的最佳时机II、123.买卖股票的最佳时机III

LeetCode121

2025-03-08 16:56:40 星期六

题目描述:力扣121
文档讲解:代码随想录(programmercarl)121. 买卖股票的最佳时机
视频讲解:《代码随想录》算法视频公开课:动态规划之 LeetCode:121.买卖股票的最佳时机1

代码随想录视频内容简记

本题和昨天的打家劫舍Ⅲ树型dp有一些相似之处,同样都是每个节点有两个状态,分别表示当前持有和不持有这只股票

首先需要明白的是:“持有”和“买入”不是一回事,“不持有”和“卖出”也不是一回事

梳理

  1. 确定dp数组的含义,dp[i][0]表示持有当前股票所能获得的最大利润为dp[i][0],dp[i][1]表示不持有当前股票所能获得的最大利润dp[i][1]

  2. 确定递推公式

    如果持有当前股票。如果dp[i][0]表示当前所能获得最大利润,那么dp[i - 1][0]表示前一个状态继续持有,-price[i]表示当前状态卖出这只股票,那么dp[i][0] = max(dp[i - 1][0], -price[i])

    如果不持有当前股票。这只股票如果卖出,那么前一天的状态一定是持有的,也就是dp[i - 1][0] + price[i],如果继续不持有当前股票,那么就是dp[i - 1][1],递推公式就是dp[i][1] = max(dp[i - 1][0], dp[i - 1][1] + price[i])

  3. 初始化dp数组,dp[0][0]表示持有当前股票,所以一定是-price[0],dp[0][1]就一定是0

  4. 确定遍历顺序,从前向后遍历即可

  5. 打印dp数组

LeetCode测试

至于最后返回的为什么是dp[prices.size() - 1][1],因为当前不持有股票的现金数量一定是大于持有股票的现金数量的

点击查看代码
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(), vector(2, 0));
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        for (int i = 1; i < prices.size(); i++) {
            // 持有当前股票
            dp[i][0] = max(dp[i - 1][0], -prices[i]);
            // 不持有当前股票
            dp[i][1] = max(dp[i - 1][0] + prices[i], dp[i - 1][1]);
            cout << dp[i][0] << endl;
        }
        return dp[prices.size() - 1][1];
    }
};

LeetCode122

题目描述:力扣122
文档讲解:代码随想录(programmercarl)122.买卖股票的最佳时机II
视频讲解:《代码随想录》算法视频公开课:动态规划,股票问题第二弹 | LeetCode:122.买卖股票的最佳时机II

代码随想录视频内容简记

之前这个题用的贪心,局部最优就是求每一天买卖股票的利润最大,全局最优就是所得到的总利润最大。如果每一天的差值大于0,那么就说明可以买入并进行卖出,直接加上这个差值作为利润

本题和121买卖股票Ⅰ的区别就在于本题的递推公式中持有股票的状态需要变化一下

因为本题可以买卖多次,那么也就是说不能像之前一样,当买入股票时用0减去prices[i],而应该用前一个不持股状态的最大利润dp[i - 1][1]来减,也就dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i])

LeetCode测试

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

    }
};

LeetCode123

题目描述:力扣123
文档讲解:代码随想录(programmercarl)123.买卖股票的最佳时机III
视频讲解:《代码随想录》算法视频公开课:动态规划,股票至多买卖两次,怎么求? | LeetCode:123.买卖股票最佳时机III

代码随想录视频内容简记

这个题和之前的两道确实是大同小异,就是因为是最多能买卖两次,那么需要定义的dp的状态就会变多

梳理

  1. 确定dp数组和含义

dp[i][0]表示不操作
dp[i][1]表示第一次持股的最大利润
dp[i][2]表示第一次不持股的最大利润
dp[i][3]表示第二次持股的最大利润
dp[i][4]表示第二次不持股的最大利润
  1. 确定递归公式

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])
  1. 初始化dp数组,这里解释一下,因为题目中并没有规定就是不可以在同一天进行买入和卖出,那么第零天就可以是,当持股dp[i][1]和dp[i][3]都是表示的-prices[0],当不持股dp[i][2]和dp[i][4]都是表示的0

dp[0][0] = 0
dp[0][1] = -prices[0]
dp[0][2] = 0;
dp[0][3] = -prices[0]
dp[0][4] = 0
  1. 确定遍历顺序,从前向后遍历,最后返回的dp[prices.size() - 1][4]即可

  2. 打印dp数组

LeetCode测试

其实dp[0][0]也是没什么用的,直接用0代替就可以。就是搞懂了原理之后也没有那么难,这是开营第三道困难的题

点击查看代码
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(), vector(5, 0));
        // dp[0][0] = 0;
        dp[0][1] = -prices[0];
        dp[0][2] = 0;
        dp[0][3] = -prices[0];
        dp[0][4] = 0;
        for (int i = 1; i < prices.size(); i++) {
            dp[i][0] = 0;
            dp[i][1] = max(dp[i - 1][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[prices.size() - 1][4];
    }
};
posted on 2025-03-08 16:56  bnbncch  阅读(208)  评论(0)    收藏  举报