买卖股票问题
文章目录
- 【动态规划】简单多状态dp问题(2)买卖股票问题
- 1. 最佳买卖股票时机含冷冻期(买卖股票Ⅰ)
- 1.1 题目解析
- 1.2 算法原理
- 1.2.1 状态表示
- 1.2.2 状态机
- 1.2.3 状态转移方程
- 1.2.4 初始化
- 1.2.5 填表顺序
- 1.2.6 返回值
- 1.3 编写代码
- 2. 买卖股票的最佳时机含手续费(买卖股票Ⅱ)
- 2.1 题目解析
- 2.2 算法原理
- 2.2.1 状态表示
- 2.2.3 状态机
- 2.2.3 状态转移方程
- 2.2.4 初始化
- 2.2.5 填表顺序
- 2.2.6 返回值
- 2.3 编写代码
- 3. 买卖股票的最佳时期限制次数(买卖股票Ⅲ)
- 3.1 题目解析
- 3.2 算法原理
- 3.2.1 状态表示
- 3.2.2 状态机
- 3.2.3 状态转移方程
- 3.2.4 初始化
- 3.2.5 填表顺序
- 3.2.6 返回值
- 3.3 编写代码
- 4. 买卖股票的最佳实际限制次数(买卖股票Ⅳ)
- 4.1 与第三题的关系
- 4.2 编写代码
传送门:力扣309. 最佳买卖股票时机含冷冻期
题目:
越难的dp问题,看示例只能起到了解题目的效果,一般推不出啥普遍的规律,所以接下来就是我们的算法原理,通过动归的思想去理解,才会豁然开朗!
我们需要通过经验 + 题目要求去决定状态表示:
再根据经验,一般dp表的其中一值就应该是答案!
综合得到状态表示:dp[i]
表示就是从起点到i坐标这些天 结束后 的最大收益
而这道题,与之前做过的题不一样的是,一个坐标的状态有三种情况,需要我们继续细化
所以,最终的状态表示为:
f[i]表示的是,从起点到 i 坐标的这些天结束后,为可交易的持有股票状态的情况下的最大收益
g[i]表示的是,从起点到 i 坐标的这些天结束后,为可交易的未持有股票状态的情况下的最大收益
h[i]表示的是,从起点到 i 坐标的这些天结束后,为不可交易的冷冻期状态的情况下的最大收益
在推导状态转移方程之前,我们要做一些准备工作
而这一幅图,就是”状态机”
同样的套路,我们需要根据已确定的dp表的值来推导 f[i] 、 g[i] 以及 h[i]的值,并且牢记他们的状态表示!
“最近一步”可以理解为“必然事件”
如果这一天结束后为f,那么前一天结束后可能是g或者f
如果这一天结束后为g,那么前一天结束后可能为g或者h
如果这一天结束后为h,那么前一天结束后一定为f
所以得出状态转移方程:
f[i] = max{g[i - 1] - prices[i], f[i - 1]};
g[i] = max{g[i - 1], h[i - 1]};
h[i] = f[i - 1] + prices[i]
在第一天结束后
从做到有,三个表一起填
最后其实处于三种状态都有可能,取较大值即可!
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[] f = new int[n];
int[] g = new int[n];
int[] h = new int[n];
f[0] = -prices[0];
for(int i = 1; i < n; i++) {
f[i] = Math.max(f[i - 1], g[i - 1] - prices[i]);
g[i] = Math.max(g[i - 1], h[i - 1]);
h[i] = f[i - 1] + prices[i];
}
return Math.max(
Math.max(f[n - 1], g[n - 1]), h[n - 1]
);
}
}
时空复杂度都为:O(N)
传送门:力扣714. 买卖股票的最佳时机含手续费
题目:
越难的dp问题,看示例只能起到了解题目的效果,一般推不出啥普遍的规律,所以接下来就是我们的算法原理,通过动归的思想去理解,才会豁然开朗!
我们需要通过经验 + 题目要求去决定状态表示:
再根据经验,一般dp表的其中一值就应该是答案!
综合得到状态表示:dp[i]
表示就是起点到坐标为 i 的位置【这些天结束后】的最大收益
而这道题,与之前做过的题不一样的是,一个坐标的状态有两种情况,需要我们继续细化
所以,最终的状态表示为:
f[i]表示的是,从起点到 i 坐标的这些天结束后,持票的情况下的最大收益
g[i]表示的是,从起点到 i 坐标的这些天结束后,未持票的情况下的最大收益
同样的套路,我们需要根据已确定的dp表的值来推导 f[i] 和 g[i] 的值,并且牢记他们的状态表示!
“最近一步”可以理解为“必然事件”
而1代表着f表怎么填,2代表着g表怎么填
所以得出状态转移方程:
f[i] = max{f[i - 1], g[i - 1] - prices[i]};
g[i] = max{f[i - 1] + prices[i] - fee, g[i - 1]};
在第一天结束后
最后一天结束后,两种情况收益的较大值
class Solution {
public int maxProfit(int[] prices, int fee) {
int n = prices.length;
int[] f = new int[n];
int[] g = new int[n];
f[0] = -prices[0];
for(int i = 1; i < n; i++) {
f[i] = Math.max(f[i - 1], g[i - 1] - prices[i]);
g[i] = Math.max(g[i - 1], f[i - 1] + prices[i] - fee);
}
return Math.max(f[n - 1], g[n - 1]);
}
}
时空复杂度都为:O(N)
传送门:力扣123.买卖股票的最佳时机 III
题目:
越难的dp问题,看示例只能起到了解题目的效果,一般推不出啥普遍的规律,所以接下来就是我们的算法原理,通过动归的思想去理解,才会豁然开朗!
我们需要通过经验 + 题目要求去决定状态表示:
再根据经验,一般dp表的其中一值就应该是答案!
综合得到状态表示:dp[i][j]
表示就是起点到坐标为 i 的位置这些天结束后交易次数为j的最大收益
而这道题,与之前做过的题不一样的是,一个坐标的状态有两种情况,需要我们继续细化
所以,最终的状态表示为:
f[i] [j]表示的是,从起点到 i 坐标的这些天结束后,持票且交易次数为 j 的情况下的最大收益
g[i] [j]表示的是,从起点到 i 坐标的这些天结束,未持票且交易次数为 j 的情况下的最大收益
同样的套路,我们需要根据已确定的dp表的值来推导 f[i] [j] 和 g[i] [j]的值,并且牢记他们的状态表示!
“最近一步”可以理解为“必然事件”
而1代表着f表怎么填,2代表着g表怎么填
所以得出状态转移方程:
f[i] [j] = max{f[i - 1] [j], g[i - 1] [j] - prices[i]};
g[i] [j] = max{g[i - 1] [j], f[i - 1] [j - 1] + prices[i]};
问题:怎么做到限制次数的?
首先,到达交易三次的前提是到达交易两次
- 那么,交易两次后,其值并没有追加在其他元素上,那么就不会增加交易次数
依此限制了交易次数
初始化:
f表:
g表:
即,g表的状态转移方程为:
从上到下两个表一起填每一行,每一行每一列一起填
最后一天结束后,f或者g状态下,各个交易次数都要算上,去最大值!
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[][] f = new int[n][3];
int[][] g = new int[n][3];
f[0][0] = -prices[0];
for(int i = 1; i < 3; i++) {
f[0][i] = -0x3f3f3f3f;
g[0][i] = -0x3f3f3f3f;
}
for(int i = 1; i < n; i++) {
for(int j = 0; j < 3; j++) {
f[i][j] = Math.max(f[i - 1][j], g[i - 1][j] - prices[i]);
g[i][j] = g[i - 1][j];
if(j >= 1) {
g[i][j] = Math.max(g[i][j], f[i - 1][j - 1] + prices[i]);
}
}
}
int min = -0x3f3f3f3f;
for(int i = 0; i < 3; i++) {
min = Math.max(min, f[n - 1][i]);
min = Math.max(min, g[n - 1][i]);
}
return min;
}
}
时空复杂度都为:O(N)
传送门:力扣188. 买卖股票的最佳时机 IV
题目:
这道题,跟第三题唯一的区别就是,第三题限制为2次,而这道题限制为k次
第三题的dp表是n × 3,填表时因为空间的限制,无法达到3次及以上
那么这道题,dp表设为n × (k + 1),填表由于空间限制,也无法达到k次以上
class Solution {
public int maxProfit(int k, int[] prices) {
int n = prices.length;
int[][] f = new int[n][k + 1];
int[][] g = new int[n][k + 1];
f[0][0] = -prices[0];
for(int i = 1; i < k + 1; i++) {
f[0][i] = -0x3f3f3f3f;
g[0][i] = -0x3f3f3f3f;
}
for(int i = 1; i < n; i++) {
for(int j = 0; j < k + 1; j++) {
f[i][j] = Math.max(f[i - 1][j], g[i - 1][j] - prices[i]);
g[i][j] = g[i - 1][j];
if(j >= 1) {
g[i][j] = Math.max(g[i][j], f[i - 1][j - 1] + prices[i]);
}
}
}
int min = -0x3f3f3f3f;
for(int i = 0; i < k + 1; i++) {
min = Math.max(min, f[n - 1][i]);
min = Math.max(min, g[n - 1][i]);
}
return min;
}
}
时空复杂度都为:O(N * k)
文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭!本文代码链接:动态规划04/src/Main.java · 游离态/马拉圈2023年6月 - 码云 - 开源中国 (gitee.com)