代码随想录笔记_动态规划_123买卖股票的最佳时机III

代码随想录二刷笔记记录

LC123.买卖股票的最佳时机III


题目

股票问题

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。

示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。
示例 4:

输入:prices = [1]
输出:0


思路分析

思路

本题和LC121,123一样,仍然存在两个基本状态,持股与不持股,由于题目规定,最多可以完成两笔交易。根据两笔交易,可以将两个基本状态进行拆分。

上述两种基本状态拆分为:

  • 第一次持股
  • 第一次不持股
  • 第二次持股
  • 第二次不持股

而考虑到示例3的输出,没有利润的情况下,选择不操作(空仓)。因此,还需考虑一种状态:不操作

注意到,题目要求不能参与多笔交易,因此状态的顺序一定是: 第一次持股 - 第二次持股 - 第三次持股 - 第四次持股

动态规划五部曲

1.确定dp数组及其下标的含义

dp[i][j] :  i 表示第 i , j [0-4] 五个状态 , dp[i][j]  i ,状态 j 的最大利润
0 : 没有操作
1 : 第一次买入
2 : 第一次卖出
3 : 第二次买入
4 : 第二次卖书

2.确定递推公式

回顾LC122,我们可知,因为股票问题至多只能买入一股,具有满仓(持有股票的状态),空仓(不持有股票的状态); 而买入和卖出操作,都是状态转移的操作。

dp[i][1] : 第 i 天买入股票的状态
dp[i][1] 由 dp[i][0] 推演而来
// 第 i 天买入股票
//状态转移:空仓 -> 满仓 , i-1 天的状态0,转移到第 i 天的状态1
dp[i][1] = dp[i-1][0] - prices[i]; 
// 第 i-1 天就持有股票,保持满仓状态1
dp[i][1] = dp[i-1][1];  
dp[i][1] = Max(dp[i-1][0] - prices[i],dp[i-1][1]);

dp[i][2] 由 dp[i][1] 推演而来
//第 i 天卖出股票
//状态转移: 满仓 -> 空仓, i-1 天的状态1,转移到第 i 天的状态2
dp[i][2] = dp[i-1][1] + prices[i]; 
// 第 i 天没有操作,保持空仓状态2
dp[i][2] = dp[i-1][2]; 
dp[i][2] = Max(dp[i-1][1] + prices[i],dp[i-1][2]);

dp[i][3] 同理
// 第 i 天买入股票
//状态转移:空仓 -> 满仓 , i-1 天的状态2,转移到第 i 天的状态3 
dp[i][3] = dp[i-1][2] - prices[i];
//第 i 天没有操作,保持满仓状态3
dp[i][3] = dp[i-1][3]; 
dp[i][3] = Max(dp[i-2][3], dp[i-2][2] - prices[i]);

dp[i][4] 同理
//第 i 天卖出股票
//状态转移: 满仓 -> 空仓, i-1 天的状态3,转移到第 i 天的状态4
dp[i][4] = dp[i-1][3] + prices[i];
// 第 i 天没有操作,保持空仓状态4
dp[i][4] = dp[i-1][4];
dp[i][4] = Max(dp[i-1][3] + prices[i], dp[i-1][4]);

3.初始化

dp[0][0] = 0; // 第 0 天没有操作
dp[0][1] = -prices[0]; // 第 0 天买入股票
dp[0][2] = 0;//第 0 天卖出股票
dp[0][3] = -prices[0];//第 0 天第二次买入股票,相当于第 0 天买入卖出又买入
dp[0][4] = 0;//第 0 天第二次卖出股票,相当于第 0 天两次的买卖

4.遍历顺序

根据递推公式,我们知道,第 i 天的状态依赖于第 i-1 天的状态,因此是从前往后 遍历。

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

5.推演分析

以示例2 prices = [1,2,3,4,5] 为例

状态0状态1状态2状态3状态4
i不操作买入1卖出1买入2卖出2
00-10-10
10max(-1,-2) = -1max(-1,-1+2) = 1max(-1,-2) = -1max(0,-1+2)= 1
20max(-1,-3) = -1max(-1,-1+3) = 2max(-1,1-3) = -1max(1,-1+3)= 2
30max(-1,-4) = -1max(-1,-1+4) = 3max(-1,2-4) = -1max(2,-1+4)= 3
40max(-1,-5) = -1max(-1,-1+5) = 4max(-1,3-5) = -1max(3,-1+5)= 4

代码实现

完整代码实现

public int maxProfit(int[] prices) {
        //5种状态
        //0:没有操作 1:第一次买入 2:第一次卖出 3:第二次买入 4:第二
        //dp初始化
        int[][] dp = new int[prices.length][5];
        //数组初始化时即为0,状态0,2,4可以注释
        dp[0][0] = 0;//第 0 天没有操作
        dp[0][1] = -prices[0]; //第 0 天买入股票
        dp[0][2] = 0; // 第 0 天卖出股票
        dp[0][3] = -prices[0];// 第 0 天第二次买入股票
        dp[0][4] = 0; // 第 0 天第二次买卖

        //遍历
        for (int i = 1;i < dp.length;i++){
            //dp[i][0]没有操作全为0,可以注释掉
            dp[i][0] = Math.max(dp[i][0],dp[i-1][0]); //第 i 天没有操作,第 i-1 天没有操作
            //dp[i-1][1] - prices[i]第 i-1 天买入股票,今天继续买,
            dp[i][1] = Math.max(dp[i-1][0] - prices[i], dp[i-1][1]);
            //dp[i-1][1] + prices[i],第i-1天买入股票.第i天卖出
            dp[i][2] = Math.max(dp[i-1][1] + prices[i], dp[i-1][2]);
            dp[i][3] = Math.max(dp[i-1][2] - prices[i], dp[i-1][3]);
            dp[i][4] = Math.max(dp[i-1][3] + prices[i], dp[i-1][4]);
        }
        return dp[prices.length-1][5-1];
    }

小结

本题增加了3个限制条件

  • 最多可完成两笔交易
  • 不能参与多笔交易
  • 可选择从头到尾不交易(示例3)

因此我们将状态拆分为5种状态,但是本质的递推公式分析,与LC122买卖股票的最佳时机II,需要理解持股不持股,即满仓空仓的状态是否会转移。如果存在买入和卖出操作,则会产生状态转移,反之,即保持状态不变即可。本质上的递推不变,终点在于分析题目和拆解状态。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值