代码随想录 day 41 动态规划 买卖股票 I II III

第九章 动态规划part08

股票问题是一个动态规划的系列问题,前两题并不难,第三题有难度。

121. 买卖股票的最佳时机

视频讲解:https://www.bilibili.com/video/BV1Xe4y1u77q
https://programmercarl.com/0121.%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BA.html

122.买卖股票的最佳时机II

视频讲解:https://www.bilibili.com/video/BV1D24y1Q7Ls
https://programmercarl.com/0122.%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BAII%EF%BC%88%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%EF%BC%89.html

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

这道题一下子就难度上来了,关键在于至多买卖两次,这意味着可以买卖一次,可以买卖两次,也可以不买卖。
视频讲解:https://www.bilibili.com/video/BV1WG411K7AR
https://programmercarl.com/0123.%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BAIII.html

121. 买卖股票的最佳时机

题目链接

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/

解题思路

dp[i][0] 第i天持有这支股票的最大现金 (实际就是找买了股票后手里最大现金)
dp[i][1] 第i天不持有这支股票的最大现金 (实际就是找到最大利润)

code

class Solution {
    

    public int maxProfit(int[] prices) {
        //1.确定dp数组以及下标的含义
        //dp[i][0] 第i天持有这支股票的最大现金 (实际就是找买了股票后手里最大现金)
        //dp[i][1] 第i天不持有这支股票的最大现金  (实际就是找到最大利润)
        //2.确定递推公式
        //dp0依赖前一天推出
        //前一天持有或今天持有 去一个手里的最大现金,这里的前一天不是一定是前一天
        //有可能是前几天手里的现金已经是最大的了
        //dp[i][0]=Math.max(dp[i-1][0],-price[i]); 
        //可以看到这里是个负数今天买入价格7的股票我的最大现金就是-7
        //dp1依赖前一天推出
        //前一天(前一天可能是前几天已经推出最大利润)卖出的最大利润 dp[i-1][1]
        //和当前天卖出的最大利润dp[i-1][0](前一天(前几天)持有股票的最大现金,这是个)+price[i] 
        //dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+price[i]);
        
        //3.如何初始化数组
        int[][] dp=new int[prices.length][2];
        //持有
        dp[0][0]=-prices[0];
        //不持有
        dp[0][1]=0;
        //4.确定遍历顺序
        //从递推公式可以看出dp[i]都是由dp[i - 1]推导出来的,那么一定是从前向后遍历
        for(int i=1;i<prices.length;i++){
            dp[i][0]=Math.max(dp[i-1][0],-prices[i]); 
            dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]);
        }
        //5.打印dp数组

        //最终求的是不持有这支股票的最大现金
        return dp[prices.length-1][1];
    }
}

122.买卖股票的最佳时机II

题目链接

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/

解题思路

第i天持有这只股票所得最多现金
因为股票I 那道题全程只能买卖一次,所以如果买入股票,那么第i天持有股票即dp[i][0]一定就是 -prices[i]
不同的是这题可以重复买,所以当第i天买入股票的时候,所持有的现金可能有之前买卖过的利润
如果是第i天买入股票,所得现金就是昨天不持有股票的所得现金 减去 今天的股票价格 即:dp[i - 1][1] - prices[i]
dp[i-1][1] 可能表示自己之前买卖过的记录, dp[i-1][1]-prices[i] 表示这次又重新买了
dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]-prices[i]);
第i天不持有这只股票的所得最多现金
dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]);

code

class Solution {
    public int maxProfit(int[] prices) {
        
        int[][] dp=new int[prices.length][2];
        dp[0][0]=-prices[0];
        dp[0][1]=0;
        for(int i=1;i<prices.length;i++){
            //第i天持有这只股票的最大金钱  ,
            //跟股票I 不同的是 dp[i-1][1]-prices[i],这里是可以重复买股票的
            //dp[i-1][1] 可能表示自己之前买卖过的记录, dp[i-1][1]-prices[i] 表示这次又从新买了
            dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]-prices[i]);
            //第i天不持有这只股票的最大金钱
            dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]);
        }
        return dp[prices.length-1][1];
    }
}

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

题目链接

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/description/

解题思路

关键在于至多买卖两次,这意味着可以买卖一次,可以买卖两次,也可以不买卖。
接来下我用动态规划五部曲详细分析一下:
1.确定dp数组以及下标的含义
一天一共就有五个状态,
0 没有操作 (其实我们也可以不设置这个状态)
1 第一次持有股票
2 第一次不持有股票
3 第二次持有股票
4 第二次不持有股票
dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大现金。
2.确定递推公式
dp[i][0]=dp[i-1][0]
其实我们可以不设置,‘0. 没有操作’ 这个状态,因为没有操作,手上的现金自然就是0, 正如我们在 121.买卖股票的最佳时机 和 122.买卖股票的最佳时机II 也没有设置这一状态是一样的。
dp[i][1]=Math.max(dp[i-1][1],-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.如何初始化
第0天没有操作 dp[0][0] =0
第0天第一次持有 dp[0][1]=-prices[i];
第0天第一次不持有 dp[0][2]=0
第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后再买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。
所以第二次买入操作,初始化为:dp[0][3] = -prices[0];
dp[0][4]=0;
4.确定遍历顺序
dp[i] 依赖dp[i-1] 推出从前向后遍历
5.举例推导dp数组
以输入[1,3,1,7]为例
image.png
dp[0][2] 为什么初始化为 -price[i] 第一次可以进行当天买卖 持有就是0 第二次 在进行买就是 -price[0]
把第一次取股票的利润推到第二次,第二次在计算出利润做一个相加
如果不能执行第二次,那么就取第一次的最大结果 如:1,2,3,4,5
代码随想录解释:
第0天第二次买入操作,初始值应该是多少呢?应该不少同学疑惑,第一次还没买入呢,怎么初始化第二次买入呢?
第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后再买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。
所以第二次买入操作,初始化为:dp[0][3] = -prices[0];

code

class Solution {
    public int maxProfit(int[] prices) {
        //dp[i][0] 持有第一支股票所得最大金钱
        //dp[i][1] 不持有第一支股票所得最大金钱
        //dp[i][2] 持有第二支股票所得最大金钱
        //dp[i][3] 不持有第二支股票所得最大金钱
        //初始化
        int[][] dp=new int[prices.length][4];
        dp[0][0]=-prices[0];
        dp[0][1]=0;
        //这里初始化理解就是第0天第一次买入然后卖出了,第0天第二次重新买入
        dp[0][2]=-prices[0];
        dp[0][3]=0;
        for(int i=1;i<prices.length;i++){
            dp[i][0]=Math.max(dp[i-1][0],-prices[i]);
            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]);
        }
        return dp[prices.length-1][3];
    }
}
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值