日常算法练习(4)股票系列问题(DP解法)

前言

股票系列算法问题时常伴随着各种“奇技淫巧”,在本文中我尽量以DP的方式来解答这类问题,同时抽象出一个大致的模版供大家参考。

买卖股票的最佳时机I

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
首先我们审题,关键点有以下几点:
1.只允许一笔交易
2.不能在买入前卖出
我们面临的情况只有两种:
1.不持有股票的时候决定买入或不买入
2.持有股票的时候决定卖出或不卖出
我们求的是一次交易的最大收益,就可以转换为在最便宜的时候买入,在最贵的时候卖出。
同时由于时间的单向流动的特性,我们不能在第五天买第一天卖,所以我们对于“最便宜”的更新以及“最贵”的更新需要同步进行。
由此可以定义出状态转移方程:第i天为止的最大收益 = Math.max(当前股票价值-第i天以前最低股票价值 , 第i天以前的最大收益)
class Solution {
    public int maxProfit(int[] prices) {
        if (prices.length == 0) return 0;
        int[] dp = new int[2];
        //dp[0]是未持有股票时候第i天选择购买需要话费的金钱
        dp[0] = prices[0];
        //dp[1]是持有股票后在第i天出售的获利
        dp[1] = Integer.MIN_VALUE;
        for (int i = 0 ; i < prices.length ; i++) {
            dp[0] = Math.min(dp[0] , prices[i]);
            dp[1] = Math.max(dp[1] , prices[i] - dp[0]);
        }
        return dp[1];
    }
}

买卖股票的最佳时机II

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
相较于第一题的改变是可以进行无限制次数的交易,那么这意味着什么呢,意味着未购买股票这种状态多了一种情况,可能是从未购买,也可能是刚卖掉,所以我们需要多记录一个状态。
class Solution {
    public int maxProfit(int[] prices) {
        if (prices.length < 2)  return 0;
        int[] dp = new int[3];
        //dp[0]记录在第i天不持有股票时候的总收益,初始化为0
        dp[0] = 0;
        //记录在第i天选择购进股票时候的总收益,初始化为第一天股价
        dp[1] = -prices[0];
        //记录在第i天选择出售股票时候的总收益,初始化为极小数字
        dp[2] = Integer.MIN_VALUE;
        for (int i = 1 ; i < prices.length ; i++) {
        	//两种情况,可能是没买过,可能是刚卖
            dp[0] = Math.max(dp[2] , dp[0]);
            dp[1] = Math.max(dp[0] - prices[i] , dp[1]);
            dp[2] = Math.max(dp[1] + prices[i] , dp[2]);
        }
        return dp[2];
    }
}

买卖股票的最佳时机III

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

相较于第二题,这里的限制条件由无限次交易改为了2次,这意味着不持有股票的情况需要更加细化,可能是从未购买,可能是第一次出售后,可能是第二次出售后,我们将状态定义为:

dp[0]:第一次购买股票时的总收益

dp[1]:第一次出售股票时的总收益

dp[2]:第二次购入股票时候的总收益

dp[3]:第二次出售股票时候的总收益

dp代表当前的总收益,总体来看状态转移方程就是进行操作(购入或出售)或保持不变。

同时最大收益可能出现在第一次出售股票或第二次出售股票。

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        if (len < 2)   return 0;
        int[] dp = new int[4];
        //初始化为在第一天购入
        dp[0] = -prices[0];
        //其余初始化为极小值,因为在第一天不存在这三种状态
        for (int i = 1 ; i < 4 ; i++) {
            dp[i] = Integer.MIN_VALUE;
        }
        for (int i = 1 ; i < len ; i++) {
        	//状态转移方程为操作(dp[当前状态] +/- prices[i])或者不操作(dp[保持状态])
            dp[0] = Math.max(- prices[i] , dp[0]);
            dp[1] = Math.max(dp[0] + prices[i] , dp[1]);
            dp[2] = Math.max(dp[1] - prices[i] , dp[2]);
            dp[3] = Math.max(dp[2] + prices[i] , dp[3]);
        }
        return Math.max(dp[1] , dp[3]);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值