注意1:本题所指的买入与卖出均为一种状态,准确来说是持有和不持有。并不代表当天一定是买入或者卖出,上述状态亦可从前一天的状态转化而来。
dp[i][0]
:第一次买入股票后的最大利润。dp[i][1]
:第一次卖出股票后的最大利润。dp[i][2]
:第二次买入股票后的最大利润。dp[i][3]
:第二次卖出股票后的最大利润。
注意2:为什么要如此进行初始化?主要是对第二次交易的状态的理解。
dp[0][0] = -prices[0]; // 第一次买入,花费了 prices[0]
dp[0][1] = 0; // 第一次卖出,但第一天不可能卖出,所以利润为0
dp[0][2] = -prices[0]; // 第二次买入,花费了 prices[0]
dp[0][3] = 0; // 第二次卖出,但第一天不可能卖出,所以利润为0
如此初始化的目的是为了确保我们在动态规划的初始阶段有一个合理的起点。具体来说,我们假设在第一天可以进行两次买入操作—即一天中可以完成多次交易(虽然这是不现实的,但这是为了确保状态转移的正确性)。这样,我们可以在后续的动态规划过程中逐步更新状态,最终得到正确的最大利润。
注意3:为什么只要返回第二天卖出状态的最大利润即可?
在我们的状态定义中,我们假设一天可以进行多次交易。正因为如此,如果当前最大利润出现在第一次交易完成之后,我们第二次交易的完成也可以在同一天进行,即当天买入当天卖出,此时完成了第二次交易且所获利润为0。由此可见,第二天卖出状态的最大利润这个值已经考虑了所有可能的交易组合,并且确保了通过两次交易可以获得的最大利润。
dp[n-1][3]
表示在最后一天(即第n-1
天)第二次卖出股票后的最大利润。- 这个状态已经考虑了所有可能的交易组合,包括同一天买入和卖出。
- 换句话说,
dp[n-1][3]
包含了所有可能的交易策略,包括:- 第一次买入和第一次卖出。
- 第二次买入和第二次卖出。
- 允许同一天买入和卖出,即可以进行多次交易。
考虑同一天买入和卖出
- 同一天买入和卖出:如果我们在某一天买入股票并在同一天卖出,这种操作的利润为0。但在动态规划中,这种操作是被允许的,因为它不会影响我们的最大利润计算。
- 最大利润:通过允许同一天买入和卖出,我们可以更灵活地计算最大利润,因为我们不限制交易的时间点,只有在最后一天第二次卖出后的状态才是我们关心的。
完整代码:
class Solution {
public:int maxProfit(vector<int>& prices) {// 状态:// 第一次买入股票的状态// 第一次卖出股票的状态// 第二次买入股票的状态// 第二次卖出股票的状态int n = prices.size();vector<vector<int>> dp(n, vector<int>(4, 0));// 初始化// 【注意】:我们将当天买入和当天卖出也视为一次交易dp[0][0] = dp[0][2] = -prices[0];for(int i = 1; i < n; i++){// 1、第一次买入可能是延续前一天买入,也可能是当天买入dp[i][0] = max(dp[i - 1][0], -prices[i]);// 2、第一次卖出可能是延续前一天卖出,也可能是当天卖出dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]);// 3、第二次买入可能是延续前一天买入,也可能是当天买入dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] - prices[i]);// 4、第二次买出可能是延续前一天卖出,也可能是当天卖出dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] + prices[i]);}return dp[n - 1][3]; // 或 return max(dp[n - 1][1], dp[n - 1][3]);}
};