前言
股票系列算法问题时常伴随着各种“奇技淫巧”,在本文中我尽量以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]);
}
}