• 代码随想录算法训练营第五十天 |123.买卖股票的最佳时机III、188.买卖股票的最佳时机IV


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

    题目链接/文章讲解:代码随想录

    视频讲解:动态规划,股票至多买卖两次,怎么求? | LeetCode:123.买卖股票最佳时机III_哔哩哔哩_bilibili

     思考:

    至多买卖两次,这意味着可以买卖一次,可以买卖两次,也可以不买卖

    1.确定dp数组(dp table)以及下标的含义

    一天一共就有五个状态,

    1. 没有操作 (也可以不设置这个状态)
    2. 第一次持有股票
    3. 第一次不持有股票
    4. 第二次持有股票
    5. 第二次不持有股票

    dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大现金。

    2.确定递推公式

    达到dp[i][1]状态,有两个具体操作:

    • 操作一:第i天买入股票了,那么dp[i][1] = dp[i-1][0] - prices[i]
    • 操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]

    同理

    dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])

    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数组的初始化

    dp[0][0] = 0

    dp[0][1] = -prices[0]

    dp[0][2] = 0 【可以理解当天买入,当天卖出】

    dp[0][3] = -prices[0]

    【第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后再买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少】

    dp[0][4] = 0

    【同理第二次卖出】

    4.确定遍历顺序

    从前向后

    5.举例推导dp数组

    代码实现: 

    1. class Solution {
    2. public:
    3. int maxProfit(vector<int>& prices) {
    4. if (prices.size() == 0) return 0;
    5. vectorint>> dp(prices.size(), vector<int>(5, 0));
    6. dp[0][1] = -prices[0];
    7. dp[0][3] = -prices[0];
    8. for (int i = 1; i < prices.size(); i++) {
    9. dp[i][0] = dp[i - 1][0];
    10. dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
    11. dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
    12. dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
    13. dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
    14. }
    15. return dp[prices.size() - 1][4];
    16. }
    17. };
    • 时间复杂度:O(n)
    • 空间复杂度:O(n×5)

    二、188.买卖股票的最佳时机IV

    题目链接/文章讲解:代码随想录

    视频讲解:动态规划来决定最佳时机,至多可以买卖K次!| LeetCode:188.买卖股票最佳时机4_哔哩哔哩_bilibili

    思考:

    这道题目可以说是动态规划:123.买卖股票的最佳时机III的进阶版,这里要求至多有k次交易

    使用二维数组 dp[i][j] :第i天的状态为j,所剩下的最大现金是dp[i][j]

    j的状态表示为:

    • 0 表示不操作
    • 1 第一次买入
    • 2 第一次卖出
    • 3 第二次买入
    • 4 第二次卖出
    • .....

    规律:除了0以外,偶数就是卖出,奇数就是买入

    1.确定dp数组(dp table)以及下标的含义

    vectorint>> dp(prices.size(), vector<int>(2 * k + 1, 0));

    2.确定递推公式

    dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][1]);

    所以dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])

     同理可以类比剩下的状态,代码如下:        

    1. for (int j = 0; j < 2 * k - 1; j += 2) {
    2. dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
    3. dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
    4. }

    3.dp数组的初始化

    1. for (int j = 1; j < 2 * k; j += 2) {
    2. dp[0][j] = -prices[0];
    3. }

    4.确定遍历顺序

    从前向后

    5.举例推导dp数组

    代码实现: 

    1. class Solution {
    2. public:
    3. int maxProfit(int k, vector<int>& prices) {
    4. if (prices.size() == 0) return 0;
    5. vectorint>> dp(prices.size(), vector<int>(2 * k + 1, 0));
    6. for (int j = 1; j < 2 * k; j += 2) {
    7. dp[0][j] = -prices[0];
    8. }
    9. for (int i = 1;i < prices.size(); i++) {
    10. for (int j = 0; j < 2 * k - 1; j += 2) {
    11. dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
    12. dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
    13. }
    14. }
    15. return dp[prices.size() - 1][2 * k];
    16. }
    17. };
    • 时间复杂度:O(n×k),其中 n 为 prices 的长度
    • 空间复杂度:O(n×k)
  • 相关阅读:
    【C语言】typedef
    ModelSim【紫光】
    Android之CompletableFuture一异步编程常用方法
    详解光耦合器是什么
    C++异常
    c++ 异常处理简单示例
    虚拟机上安装docker,并安装flink镜像
    双指针算法解决 移动零 和 复写零问题
    Java IO包中字符流Piped和CharArray简介说明
    [Java Framework] 解决监听ContextRefreshedEvent事件执行多次问题
  • 原文地址:https://blog.csdn.net/weixin_60353640/article/details/133465496