代码随想录day 48 动态规划 买卖股票的最佳时机
题121 买卖股票的最佳时机1
1,暴力解法(会超时),贪心算法可以解答此题。
2,动态规划如何解决呢?
3,定义dp数组:第i天买还是不买,两个状态。
* dp[i][0] 表示第i天持有股票的状态下手头的最大现金。(可以理解为最开始手头现金为0)
* dp[i][1]表示第i天不持有股票的状态下手头的最大现金。
4,确定递推公式:
两种情况,(1)第i 天持有股票,那么这个股票可能是之前就买入的,也可能是当天买入的, 如果是之前就买入的则dp[i] [0]= dp[i - 1] [0](当天不进行任何操作,则手里的现金保持不变); 这个股票也有可能是当天买入的,此时dp[i][0] = - prices[i], 买入股票后手里的现金为负数,因为初始认为为0。dp[i]取两者的最大值。
(2)类似的,第i天不持有股票,那么可以是之前就卖掉了股票,也可能是当天卖掉了股票(此时前一天一定是持有的状态)。dp[i][1] = max(dp[i - 1][1]) , dp[i - 1][0] + prices[i])。
5,将dp数组打印并分析之后其实dp[i][0]就是记录的买入的最低价,dp[i][1]记录的是卖出的最大利润。
6,一个困惑的问题:会不会买卖多次,其实最后记录的结果一定是低买高出的,但在遍历的过程中计算了每次的买入和卖出,但保留到最后的其实一直是最低价的买入和最大差价的卖出。
因为比如[7, 1, 5, 3, 6, 4],初始化dp[0][0] = -7, dp[0][1] = 0, 第二天遇到了最低价格,此时dp[1][0] = max(-7, -1) = -1, 再接着往后遍历的时候如果没有遇到价格低于1的,dp[i][0]会一直延续为-1,(相当于保留着在价格为1时买入的状态)。对于卖出,比如第二天dp[1][1] = max(-7 + 1 , 0) = 0(延续第一天的不持有状态,因为如果当天卖出现金为-6,比0小), 到了第三天 dp[2][1] = max(-1 + 5, 0)=4 , -1是前一天持有股票的现金(要卖出必须取前面持有的状态),第4天股票价格为3,dp[3][1] = max(-1 + 3, 4) = 4,注意此时4是延续之前的状态,说明第4天并不能真正卖股票。遍历到后面遇到股票价格更好的时候会更新不持有的现金,因此保留下来的一定是最大的利润差,而且不会出现多次买卖的问题(除非最低买入价和最高卖出价重复出现)
其实当天持有股票的状态已经可以反应出来不会重复买卖了,因为如果买入则dp[i][0] = - prices[i], 用初始0直接减当前的价格的。
class Solution {
//贪心算法:找到左边最小值,取利润最大值。
// public int maxProfit(int[] prices) {
// if(prices.length == 1) return 0;
// int min = prices[0];
// int maxProfit = 0;
// for(int i = 1; i < prices.length; i++) {
// if(prices[i] >= min) {
// maxProfit = Math.max(prices[i] - min, maxProfit);
// }
// min = Math.min(min, prices[i]);
// }
// return maxProfit;
// }
//动态规划算法
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][2];
dp[0][0] = -prices[0]; //表示初始持有股票,代表第一天就要买入,手里的现金就是-prices[0].
dp[0][1] = 0; //表示初始不持有股票,手里的现金为0
for(int i = 1; i < prices.length; i++) {
dp[i][0] = Math.max(-prices[i], dp[i - 1][0]); //第i天持有股票,手里现金的最大值。
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]); //第i天不持有股票,手里现金的最大值。
}
return dp[prices.length - 1][1];
}
}
题122 买卖股票的最佳时机2
1,本题和121题的区别在于允许多次买卖,保持不变的是手上只能有一只股票存在。
2,那么递推公式的区别在于持有股票时买入的情况,也就是dp[i][0]的其中一种情况,如果在第i天买入,则买入时手头的现金不再一直是初始的0,而应该是之前不持有股票时的最大现金dp[i - 1][1] , 因此dp[i][0] = max(dp[i - 1][1] - prices[i], dp[i - 1][0])。(上一题是0-prices[i],其实这里就是区别单次买卖和多次买卖的地方)。
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++) {
dp[i][0] = Math.max(dp[i - 1][1] - prices[i], dp[i - 1][0]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
}
return dp[prices.length - 1][1];
}
}