买卖股票的最佳时机1&2&3&4

预计周六笔者会整理一下01背包问题和完全背包问题的遍历顺序。后续还会有二叉树的遍历方式和回溯的遍历方式(其实二叉树和回溯的遍历都是一样的,回溯问题都可以变成一个二叉树的问题,来源于代码随想录)

如下的几个方法在代码随想录都会有更简洁的写法,这里主要考虑的是动态规划的思路。

买卖股票的最佳时机1

class Solution {
    public int maxProfit(int[] prices) {
        //dp[i][]数组表示第 i 天是否持有股票状态下,所拥有的最大现金数, 
        //dp[i][0]表示第 i 天持有股票时,所拥有的最大现金数
        //dp[i][1]表示第 i 天不持有股票时,,所拥有的最大现金数
        int[][] dp = new int[prices.length][2];

        //初始化,第1天开始时现金数为0
        //如果持有股票,那就是第一天(赊账)买的,所以现金为 0 - prices[0];
        dp[0][0] =  -prices[0];
        //如果不买那就还是0
        dp[0][1] =  0;

        for(int i = 1; i < prices.length; i++){
            //第i天持有股票有两种状态,可能是第 i 天买的股票,也可能是在前面就已经买了
            //第i天买股票的话,之前的现金一直是0, 第i天(赊账)买,现金为 0 - prices[i]
            //如果前面就已经买了,那就是跟上一天的状态一样,dp[i-1][0]
            dp[i][0] = Math.max(-prices[i], dp[i-1][0]);

            //第i天不持有股票也有两种状态,可能是第i天卖出去的,也可能是在前面就卖了
            //第i天卖出时,前一天持有股票所拥有的现金数 + 第i天卖出去的股票现金数 就是所拥有的现金数
            //前面就卖出去的话,那就是跟上一天的状态一样,dp[i-1][1]
            dp[i][1] = Math.max(dp[i-1][0]+prices[i], dp[i-1][1]);
        }
        //最终返回第最后一天的状态,卖了还是没卖,取最大值即可
        return Math.max(dp[prices.length-1][0], dp[prices.length-1][1]);
    }
}

买卖股票的最佳时机2

class Solution {
    public int maxProfit(int[] prices) {
        //dp数组定义同上
        int[][] dp = new int[prices.length][2];

        //初始化第一天
        dp[0][0] = -prices[0];
        dp[0][1] = 0;

        for(int i = 1; i < prices.length; i++){
            //这里和买卖股票的最佳时机1不同的是,买卖多次
            //那么当天买的状态就会有差别,因为买的时候,前一天不持有股票状态时肯定是有现金的,即dp[i-1][1]
            //用第i-1天剩下的现金在第i天买价格为prices[i]的股票,现金剩余dp[i-1][1]-prices[i]
            dp[i][0] = Math.max(dp[i-1][1]-prices[i], dp[i-1][0]);
            dp[i][1] = Math.max(dp[i-1][0]+prices[i], dp[i-1][1]);
        }
        return Math.max(dp[prices.length-1][0], dp[prices.length-1][1]);
    }
}

买卖股票的最佳时机3

代码随想录的代码会更简洁一些,这里只是笔者觉得较为清晰的思路。笔者这里是用数组的两维表示第几次买卖的状态,其实按照代码随想录,只用一维表示状态即可。(天数这个状态不可省略)

class Solution {
    public int maxProfit(int[] prices) {
        //这里稍微有点区别,dp[i][j][k]
        //含义为,在第i天,当前交易为第k次交易时,j=0或者1表示是持有还是不持有
        int[][][] dp = new int[prices.length][2][2];

        //第一天初始化,你可以想象在第一天同时买卖两次
        //那么第一次持有,买进就是0-prices[0]
        //第一次卖出,就是当前剩余的钱0-prices[0] + 卖出的利润prices[0] = 0
        //第二次同理
        dp[0][0][0] = -prices[0];
        dp[0][1][0] = 0;
        dp[0][0][1] = -prices[0];
        dp[0][1][1] = 0;

        //第二天开始
        for(int i = 1; i < prices.length; i++){
            //第i天第一次买卖需要单独放出来,因为第i天第一次买的时候是赊账买的,剩余的钱为-prices[i],
            //或者第i天前面已经第一次买入了,那就是dp[i-1][0][0]
            dp[i][0][0] = Math.max(-prices[i], dp[i-1][0][0]);

            //第i天不持有,那么就是两种情况,第i天卖出,或者是在第i天之前就卖出了
            //第i天卖出,那么就是i-1天持有状态下(j=0)的金钱 + 第i天卖出的利润prices[i]
            //第i天之前就卖出,那么第i天保持i-1天的不持有状态下(j=1)的金钱
            dp[i][1][0] = Math.max(dp[i-1][0][0]+prices[i], dp[i-1][1][0]);

            //注意!!!!!此处的dp[i-1][1][0]-prices[i]中的dp[i-1][1][0]
            //表示的是前一天为状态[1][0]的现金,即第一次卖出,前一天不持有时的状态
            //不要用前一天不持有的状态[1][1],因为有可能买入的前一天在第一次卖出的后面
            //可以这么理解,第一次卖出了,第一次卖出的状态会持续到第二次买入,直接用第一次卖出剩余的钱减去第二次买入的钱即可
            dp[i][0][1] = Math.max(dp[i-1][1][0]-prices[i], dp[i-1][0][1]);
            dp[i][1][1] = Math.max(dp[i-1][0][1]+prices[i], dp[i-1][1][1]);
        }

        //最后最大利润有可能是第一次卖出,也有可能是第二次卖出,但是第二次卖出的利润包含了第一次卖出的利润
        //因为你可以再最后一天完成第二次买入和第二次卖出(没有亏钱),相当于还是只做了第一次的买卖
        // return Math.max(dp[prices.length-1][1][0], dp[prices.length-1][1][1]);
        return dp[prices.length-1][1][1];
    }
}

买卖股票的最佳时机4

class Solution {
    public int maxProfit(int k, int[] prices) {
        int[][][] dp = new int[prices.length][2][k];

        //初始化见题目3
        for(int j = 0; j < k; j++){
            dp[0][0][j] = -prices[0];
        }

        for(int i = 1; i < prices.length; i++){
            //题目3中说过了,第一次持有时,买入时剩余的金钱会跟后面不同,因为第一次买入前现金为0,后面是有现金的。因此需要分开
            dp[i][0][0] = Math.max(-prices[i], dp[i-1][0][0]);
            dp[i][1][0] = Math.max(dp[i-1][0][0]+prices[i], dp[i-1][1][0]);

            //后面的k-1次买卖
            for(int m = 1; m < k; m++){
                dp[i][0][m] = Math.max(dp[i-1][1][m-1]-prices[i], dp[i-1][0][m]);
                dp[i][1][m] = Math.max(dp[i-1][0][m]+prices[i], dp[i-1][1][m]);
            }
        }

        //直接返回第i天,第k次买卖中,不持有股票(j = 1)状态下,的现金钱数
        //至于为什么只返回第k次,而不是k次所有的现金钱数取最大值,题目3说过了。因为最后已经包含了前面的所有了
        return dp[prices.length-1][1][k-1];
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值