代码随想录算法训练营第36天|188.买卖股票的最佳时机IV、309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费

LeetCode188

2025-03-09 17:36:31 星期日

题目描述:力扣188
文档讲解:代码随想录(programmercarl)188.买卖股票的最佳时机IV
视频讲解:《代码随想录》算法视频公开课:动态规划来决定最佳时机,至多可以买卖K次!| LeetCode:188.买卖股票最佳时机4

代码随想录视频内容简记

本题和上一题的主要区别就是最多可以买卖k次,不是两次了。其实一看到这里我想到了递归,但是其实也不用,直接新建2k个状态的数组就可以,然后用for循环进行初始化和递推即可

梳理

  1. 确定dp[i][j]数组的含义,表示第i天持有或者不持有所能得到的最大利润是dp[i][j]

  2. 确定递推公式,首先明确的,奇数表示持有,偶数表示不持有


for (int j = 0; j < 2 * k; j+=2) {
	dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
	dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
}
  1. 初始化dp数组,这个也用for循环,在奇数持有的情况下,都初始化为-prices[i],在偶数不持有的情况下,都是0就不用再单独初始化

for (int j = 1; j < 2 * k; j+=2) {
	dp[i][j] = -prices[0];
}
  1. 确定遍历顺序,遍历仍然是从前向后即可

  2. 打印dp数组

LeetCode测试

这是第四道困难的题,写的时候注意小细节,一开始又j++去了,说找不到哪里的问题

点击查看代码
class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));
        for (int j = 1; j < 2 * k; j+=2) {
            dp[0][j] = -prices[0];
        }
        for (int i = 1; i < prices.size(); i++) {
            // 递推
            for (int j = 0; j < 2 * k; j+=2) {
                dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
                dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
            }
        }
        return dp[prices.size() - 1][2 * k];
        

    }
};

LeetCode309

题目描述:力扣309
文档讲解:代码随想录(programmercarl)309.最佳买卖股票时机含冷冻期
视频讲解:《代码随想录》算法视频公开课:动态规划来决定最佳时机,这次有冷冻期!| LeetCode:309.买卖股票的最佳时机含冷冻期

代码随想录视频内容简记

冷冻期:卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

这个题最特殊的地方就是加上了冷冻期,这个冷冻期主要是限制了不能在卖出的第二天就买入。在冷冻期之后可能会有一个很长的保持卖出股票的状态,也可能在冷冻期之后第二天就买入,这两种都是可以的

所以,主要分为四个状态

  1. 保持股票

  2. 保持股票卖出

  3. 卖出股票

  4. 冷冻期

为什么把之前的不保持股票拆分开来时保持股票卖出卖出股票?就是因为如果有冷冻期的前一个状态一定是卖出股票,不是保持股票卖出的状态,另外冷冻期之后会有一个“空窗期”,这个状态不能操作股票,所以只能是保持股票卖出

梳理

  1. 确定dp[i][j]数组的定义,表示第i天状态j所能获得的最大利润

  2. 确定递推公式,

保持股票___卖出股票___冷冻期___|保持股票卖出___|___|___|__|__保持股票

  • 保持股票,其前一个状态可以还是保持股票,也可以是保持股票卖出,也可以是冷冻期,共有三种

  • 保持股票卖出,其前一个状态可以还是保持股票卖出,就是延续,也可以是冷冻期,

  • 卖出股票的前一个状态一定是保持股票

  • 冷冻期的前一个状态一定是卖出股票


dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i], dp[i - 1][3] - prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
dp[i][2] = dp[i - 1][0] + prices[i];
dp[i][3] = dp[i - 1][2]
  1. 初始化dp数组,也就是初始化第0天的。这里要注意的是,因为第0天的保持股票卖出,卖出股票和冷冻期都是非法的,所以不能从定义上考虑,直接从递推公式推导的角度进行初始化即可

dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[0][2] = 0;
dp[0][3] = 0;
  1. 确定遍历顺序,从前向后即可。

  2. 打印dp数组

LeetCode测试

这里需要注意的是,最后的四个状态中有三个都是未持有股票的状态,所以需要返回的是他们三个的最大值

点击查看代码
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(), vector<int>(5, 0));
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        dp[0][2] = 0;
        dp[0][3] = 0;
        for (int i = 1; i < prices.size(); i++) {
            dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][1] - prices[i], dp[i - 1][3] - prices[i]));
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
            dp[i][2] = dp[i - 1][0] + prices[i];
            dp[i][3] = dp[i - 1][2];
            // cout << dp[i][j] << ' ';
        }
        return max(max(dp[prices.size() - 1][1], dp[prices.size() - 1][2]), dp[prices.size() - 1][3]);
    }
};

LeetCode714

题目描述:力扣714
文档讲解:代码随想录(programmercarl)714.买卖股票的最佳时机含手续费
视频讲解:《代码随想录》算法视频公开课:动态规划来决定最佳时机,这次含手续费!| LeetCode:714.买卖股票的最佳时机含手续费

代码随想录视频内容简记

本题和买卖股票Ⅱ的差别就在于一个手续费,所以只需要修改核心的部分就是不持有股票的状态

买卖股票系列完结

LeetCode测试

点击查看代码
class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        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][1], dp[i - 1][0] + prices[i] - fee);
        }
        return dp[prices.size() - 1][1];
    }
};
posted on 2025-03-09 17:36  bnbncch  阅读(135)  评论(0)    收藏  举报