一、思路讲解

1. 当前操作

遍历到第i天时,根据当天是否持有股票,分别计算两种状态下的最大现金持有量(dp[i][0]表示第i天无股票时的最大现金,dp[i][1]表示第i天有股票时的最大现金)。

2. 子问题

已知前i-1天持有/不持有股票的最大现金(dp[i-1][0]dp[i-1][1]),推导第i天持有/不持有股票的最大现金,最终通过最后一天不持有股票的最大现金(dp[n-1][0])得到最大收益(因为最后一天持有股票无法获利)。

3. 下一个子问题

  • 若第i不持有股票:要么前一天也不持有(保持状态),要么前一天持有且当天卖出(卖出后现金增加当天股价);
  • 若第i持有股票:要么前一天已持有(保持状态),要么前一天不持有且当天买入(买入后现金减少当天股价)。

二、状态转移方程讲解

  1. 状态定义:dp[i][0]:第i天结束时不持有股票的最大现金;dp[i][1]:第i天结束时持有股票的最大现金。
  2. 状态转移:第i天不持有股票: dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) 解释:取“前一天不持有股票的现金”和“前一天持有股票、当天卖出后现金(前一天持有现金+当天股价)”中的较大值。第i天持有股票: dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]) 解释:取“前一天持有股票的现金”和“前一天不持有股票、当天买入后现金(前一天不持有现金-当天股价)”中的较大值。
  3. 初始状态:第0天不持有股票:dp[0][0] = 0(无操作,现金为0);第0天持有股票:dp[0][1] = -prices[0](买入股票,现金为负的股价)。

最终结果取dp[n-1][0](最后一天不持有股票的最大现金),若结果为负则返回0(无收益)。

三、代码实现

#include <algorithm>
#include <vector>
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int m = prices.size();
        vector<vector<int>>dp(m,vector<int>(2,0));// 枚举到第 i 天有股票/没有股票 手里的钱
        // dp[i][0] 手里没有股票时,手里的钱   dp[i][1] 手里有股票时手里的钱
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for(int i = 1;i < m;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[m-1][0];
    }
};

典型的状态机dp问题