LeetCode 第 123 题:买卖股票的最佳时机 III(动态规划)

第 1 步:状态定义
  • 状态定义:dp[i][j] 表示在 [0, i] 区间里(这个状态依然是前缀性质的),状态为 j 的最大收益。
    说明:j 的含义如下。

1、j = 0:还未开始交易;

2、j = 1:第 1 次买入一支股票;

3、j = 2:第 1 次卖出一支股票;

4、j = 3:第 2 次买入一支股票;

5、j = 4:第 2 次卖出一支股票。

第 2 步:状态转移方程

“状态转移方程”可以用下面的图表示,它的特点是:状态要么停留,要么向后面走,状态不能回退。
在这里插入图片描述
具体表示式请见代码注释。

第 3 步: 思考初始化

第 0 天的时候只能初始化两个状态,而状态 3 (表示第 2 次持股)只能赋值为一个不可能的数。

注意:只有在之前的状态有被赋值的时候,才可能有当前状态

第 4 步: 思考输出

最后一天不持股的状态都可能成为候选的最大利润。

Java 代码:

public class Solution {

    public int maxProfit(int[] prices) {
        int len = prices.length;
        if (len < 2) {
            return 0;
        }

        // dp[i][j] ,表示 [0, i] 区间里,状态为 j 的最大收益
        // j = 0:什么都不操作
        // j = 1:第 1 次买入一支股票
        // j = 2:第 1 次卖出一支股票
        // j = 3:第 2 次买入一支股票
        // j = 4:第 2 次卖出一支股票

        // 初始化
        int[][] dp = new int[len][5];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];

        // 3 状态都还没有发生,因此应该赋值为一个不可能的数
        for (int i = 0; i < len; i++) {
            dp[i][3] = Integer.MIN_VALUE;
        }

        // 状态转移只有 2 种情况:
        // 情况 1:什么都不做
        // 情况 2:由上一个状态转移过来
        for (int i = 1; i < len; i++) {
            // j = 0 的值永远是 0
            dp[i][0] = 0;
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
            dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
            dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
            dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
        }
        // 最大值只发生在不持股的时候,因此来源有 3 个:j = 0 ,j = 2, j = 4
        return Math.max(0, Math.max(dp[len - 1][2], dp[len - 1][4]));
    }
}
第 5 步: 思考状态压缩

因为今天的值只参考了昨天的值(并且是其它状态昨天的值),或者使用滚动数组,或者直接去掉第 1 维。

Java 代码:

public class Solution {

    public int maxProfit(int[] prices) {
        int len = prices.length;
        if (len < 2) {
            return 0;
        }

        // dp[i][j] ,表示 [0, i] 区间里,状态为 j 的最大收益
        // j = 0:什么都不操作
        // j = 1:第 1 次买入一支股票
        // j = 2:第 1 次卖出一支股票
        // j = 3:第 2 次买入一支股票
        // j = 4:第 2 次卖出一支股票

        int[] dp = new int[5];
        dp[1] = -prices[0];
        // 3 状态都还没有发生,因此应该赋值为一个不可能的数
        dp[3] = Integer.MIN_VALUE;

        for (int i = 1; i < len; i++) {
            dp[0] = 0;
            dp[1] = Math.max(dp[1], dp[0] - prices[i]);
            dp[2] = Math.max(dp[2], dp[1] + prices[i]);
            dp[3] = Math.max(dp[3], dp[2] - prices[i]);
            dp[4] = Math.max(dp[4], dp[3] + prices[i]);
        }
        return Math.max(0, Math.max(dp[2], dp[4]));
    }
}
  • 差分思想没有说。
  • 两次交易不能有重合,因此可以转化为两个第 121 题:枚举分割点,分别进行一笔交易赚的最大利润,即为这种情况下的最大利润。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值