力扣爆刷第130天之动态规划五连刷(买卖股票问题全家桶)
文章目录
总结:
对于买卖股票问题,是一个特殊的问题,每一天都有两种状态:
即持有股票,和不持有股票。
每一种状态又有两种选择:
即今天才持有,和之前就已经持有。或者今天才不持有,和之前就已经不持有。
搞清楚这个就简单了,因为动态规划无非就是状态与选择,剩下的变体就是买卖次数的不同。
一、121. 买卖股票的最佳时机
题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/
思路:买卖股票的最佳时机,本题只能买卖一次,求余额最多。自然就是每一天有两种状态,持有或者不持有。
持有又有两种选择,今天才持有和之前就已经持有,递推公式为dp[i][0] = max(dp[i-1][0], -nums[i])。
不持有又有两种选择,今天才不持有和之前就已经不持有,递推公式为dp[i][1] = max(dp[i-1][1], dp[i][0]+nums[i])。
另外:本题贪心也可以做,直接遍历数组,记录每一个位置的最大最小值,然后记录最大值与最小值的差值。
class Solution {
public int maxProfit(int[] prices) {
int a = -prices[0], b = 0;
for(int i = 1; i < prices.length; i++) {
a = Math.max(a, - prices[i]);
b = Math.max(b, a + prices[i]);
}
return b;
}
}
贪心
class Solution {
public int maxProfit(int[] prices) {
int min = prices[0], max = 0;
for(int i = 1; i < prices.length; i++) {
min = Math.min(min, prices[i]);
max = Math.max(max, prices[i] - min);
}
return max;
}
}
二、122. 买卖股票的最佳时机 II
题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/
思路:这个和上一题的区别在于交易的次数不再限制,那么持有股票的逻辑就要修改一下,依然是之前就持有,和今天才持有,只不过今天才持有需要用之前就不持有时手中的余额减去今天的价格,即为今天才持有。
另外本题依然可以使用谈心来做,做的话,收集相邻两天的差值,进行累加,如果小于0,从新开始。
class Solution {
public int maxProfit(int[] prices) {
int[] dp = new int[2];
dp[0] = -prices[0];
for(int i = 1; i < prices.length; i++) {
dp[0] = Math.max(dp[0], dp[1] - prices[i]);
dp[1] = Math.max(dp[1], dp[0] + prices[i]);
}
return dp[1];
}
}
三、123. 买卖股票的最佳时机 III
题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/description/
思路:本题和上一题的区别在于,限定买卖次数,最多只能买卖2次,所以每一天有两种状态,即第一次买卖和第二次买卖,买卖又分为买与卖,状态还是之前就已经持有和今天才持有。第一次与第二次买入是有区别的,区别在于第一次买入的今天才持有是没有之前的不持有的状态的,或者说是0,而第二次买入的今天才持有是和第一次买入的不持有相关的。
class Solution {
public int maxProfit(int[] prices) {
int[] dp = new int[4];
dp[0] = -prices[0];
dp[2] = -prices[0];
for(int i = 1; i < prices.length; i++) {
dp[0] = Math.max(dp[0], -prices[i]);
dp[1] = Math.max(dp[1], dp[0] + prices[i]);
dp[2] = Math.max(dp[2], dp[1] - prices[i]);
dp[3] = Math.max(dp[3], dp[2] + prices[i]);
}
return dp[3];
}
}
四、188. 买卖股票的最佳时机 IV
题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/description/
思路:前面的几道题目都是关于买卖次数的变体,本题也是,本题把买卖次数改为了最多k次,其实做法还是一样的,只不过需要把K次都遍历出来,之前次数少,比如2次都是手动写的。
class Solution {
public int maxProfit(int k, int[] prices) {
int[] dp = new int[2 * k];
for(int i = 0; i < dp.length; i += 2) {
dp[i] = -prices[0];
}
for(int i = 1; i < prices.length; i++) {
dp[0] = Math.max(dp[0], -prices[i]);
dp[1] = Math.max(dp[1], dp[0] + prices[i]);
for(int j = 2; j < dp.length; j += 2) {
dp[j] = Math.max(dp[j], dp[j-1] - prices[i]);
dp[j+1] = Math.max(dp[j+1], dp[j] + prices[i]);
}
}
return dp[2*k-1];
}
}
五、309. 买卖股票的最佳时机含冷冻期
题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/description/
思路:本题特别之处在于,除了买卖股票之外还加了冷冻期了,卖了股票后要隔一天才能再买入,那此时买入依赖的前置条件正是两天前不持有时的状态,所以要往前依赖两天,所以注意初始化,单独初始化前两天即可。
class Solution {
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][2];
for(int i = 0; i < prices.length; i++) {
if(i == 0) {
dp[i][0] = -prices[i];
continue;
}
if(i == 1) {
dp[i][0] = Math.max(dp[i-1][0], -prices[i]);
dp[i][1] = Math.max(dp[i-1][1], dp[i][0] + prices[i]);
continue;
}
dp[i][0] = Math.max(dp[i-1][0], dp[i-2][1] - prices[i]);
dp[i][1] = Math.max(dp[i-1][1], dp[i][0] + prices[i]);
}
return dp[prices.length-1][1];
}
}