LeetCode 121 买卖股票的最佳时机
思路
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
这道题我们用贪心算法做过,这次用动态规划分析。注意只能买入一次股票。
考虑动态规划五部曲:
- 确定dp数组及其下标含义:
dp[i][0]表示到第i天(含第i天)持有股票手中的最大现金数,dp[i][1]表示到第i天(含第i天)不持有股票手中的最大现金数。 - 确定递推公式:
- 如果第i天持有股票,那么要么第i-1天也持有,要么前i-1天不持有而第i天买入。所以
dp[i][0] = max(dp[i-1][0], -prices[i]) - 如果第i天不持有股票,那么要么第i-1天也不持有,要么第i-1天持有了但第i天卖出了股票。所以
dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i])
- 如果第i天持有股票,那么要么第i-1天也持有,要么前i-1天不持有而第i天买入。所以
- dp数组如何初始化:
dp[0][0]初始化为-prices[0],dp[0][1]初始化为0 - 确定遍历顺序:从小到大遍历
- 举例推导dp数组
解法
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 < dp.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]);
}
return Math.max(dp[dp.length-1][0], dp[dp.length-1][1]);
}
}
LeetCode 122 买卖股票的最佳时机II
思路
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
这次可以不限制次数地买卖股票,同样我们考虑动态规划五部曲:
- 确定dp数组及其下标含义:
dp[i][0]表示到第i天(含第i天)持有股票手中的最大现金数,dp[i][1]表示到第i天(含第i天)不持有股票手中的最大现金数。 - 确定递推公式:
- 如果第i天持有股票,那么要么第i-1天也持有,要么前i-1天不持有而第i天买入。所以
dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]) - 如果第i天不持有股票,那么要么第i-1天也不持有,要么第i-1天持有了但第i天卖出了股票。所以
dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i])
- 如果第i天持有股票,那么要么第i-1天也持有,要么前i-1天不持有而第i天买入。所以
- dp数组如何初始化:
dp[0][0]初始化为-prices[0],dp[0][1]初始化为0 - 确定遍历顺序:从小到大遍历
- 举例推导dp数组
解法
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 < dp.length; i++) {
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] - prices[i]);
dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i]);
}
return Math.max(dp[dp.length-1][0], dp[dp.length-1][1]);
}
}
LeetCode 123 买卖股票的最佳时机III
思路
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
这次我们只能买卖两次股票,所以对于同一天有五种状态,未持有,第一次持有,第一次持有后卖出,第二次持有,第二次持有后卖出。所以dp数组的第二个维度有5个值。下面考虑动态规划五部曲:
- 确定dp数组及其下标含义:
dp[i][0]表示到第i天(含第i天)从未持有股票手中的最大现金数dp[i][1]表示到第i天(含第i天)正第一次持有股票手中的最大现金数。dp[i][2]表示到第i天(含第i天)持有过一次且已卖出股票手中的最大现金数dp[i][3]表示到第i天(含第i天)正第二次持有股票手中的最大现金数dp[i][4]表示到第i天(含第i天)第二次持有后已卖出股票手中的最大现金数
- 确定递推公式:
- 如果第i天从未持有股票,那么之前i-1天也从未持有。所以
dp[i][0] = dp[i-1][0] - 如果第i天正第一次持有,那么要么第i-1天也正第一次持有,要么前i-1天不持有而第i天买入。所以
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]) - 如果第i天持有过一次且已卖出,那么要么第i-1天也持有过一次且已卖出,要么第i-1天正第一次持有而第i天卖出。所以
dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i]) - 如果第i天正第二次持有,那么要么第i-1天也正第二次持有,要么第i-1天持有过一次且已卖出而第i天买入。所以
dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i]) - 如果第i天第二次持有后已卖出,那么要么第i-1天也第二次持有后已卖出,要么第i-1天正第二次持有但第i天卖出。所以
dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i])
- 如果第i天从未持有股票,那么之前i-1天也从未持有。所以
- dp数组如何初始化:
dp[0][0]初始化为0,dp[0][1]初始化为-prices[0],dp[0][2]初始化为0,dp[0][3]初始化为-prices[0],dp[0][4]初始化为0。 - 确定遍历顺序:i从小到大遍历,j从0到4遍历
- 举例推导dp数组
解法
class Solution {
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][5];
dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[0][2] = 0;
dp[0][3] = -prices[0];
dp[0][4] = 0;
for (int i = 1; i < dp.length; i++) {
dp[i][0] = dp[i-1][0];
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]);
dp[i][4] = Math.max(dp[i-1][4], dp[i-1][3] + prices[i]);
}
return dp[dp.length-1][4];
}
}
今日收获总结
今日学习一个小时,动态规划股票问题的关键是搞清楚每天有几种买卖股票的状态,在状态之间做递推。