1. 题目来源
股票问题合集:
[Edp] lc121. 买卖股票的最佳时机(dp)
[Mdp] lc122. 买卖股票的最佳时机 II(dp+贪心+状态机+思维)
[Mdp] lc309. 买卖股票的最佳时机含冷冻期(状态机dp+股票买卖模型+经典问题)
[Hdp] lc123. 买卖股票的最佳时机 III(dp+前后缀分解)
[Hdp] lc188. 买卖股票的最佳时机 IV(状态机模型dp+线性dp+空间优化)
2. 题目解析
该股票问题:可以买卖无限次,每笔交易不能重叠(指买入下支股票前需要将手中股票卖出)
思路:
- 理解一个买入卖出方式即能解决该问题
- 第 3 天买入,第 6 天卖出,那么价值为 v 6 − v 3 v6 - v3 v6−v3,其等价于 v 4 − v 3 + v 5 − v 4 + v 6 − v 5 = v 6 − v 3 v4- v3 +v5 - v4 + v6 - v5=v6-v3 v4−v3+v5−v4+v6−v5=v6−v3
- 抽象来讲即为:如果 i i i 天买入 j j j 天卖出,那么所获利润为 p j − p i p_j-p_i pj−pi,这个可以分解为 p i + 1 − p i + p i + 2 − p i + 1 + . . . + p j − 1 − p j − 2 − p j − 1 = p j − p i p_{i+1} - p_i + p_{i+2} - p_{i+1} +...+p_{j -1} - p_{j - 2} - p_{j -1} = p_j-p_i pi+1−pi+pi+2−pi+1+...+pj−1−pj−2−pj−1=pj−pi 这两者也是等价的
- 故我们相当于将一个区间段的交易分解为每一个小区间之间的交易总和。这里的区间指的是跨越天数的股票买卖,这里的小区间就是当天买入,次天卖出这样的操作。每一个区间段交易均可以这样分解
- 那么问题转化为:当天买入,次天卖出(这就是小区间)时
- 收益如果为正,意味着这次操作就可以获利,那么隔很多天买卖股票的操作就可以包含这个小区间,这段区间对最后收益贡献一个正值
- 收益如果为负,那么如果我们大区间包含了该段区间势必导致我们获利减小,故需要跳过该小区间,这段区间对最后收益贡献为 0
- 这些小区间收益为正的操作就是最优操作,其中能够首尾相连起来的就可以将其合并成一个大区间
这个转化思想还是挺重要的,留意。
总结: res += max{0,第 i 天的价格 - 前 i - 1 天的价格}
同时,本题的通解其实是需要考虑状态机 dp 的,类似于:[Hdp] lc188. 买卖股票的最佳时机 IV(状态机模型dp+线性dp+空间优化) 中的状态机 dp 模型一样。
思路:状态机dp
-
第
i
天来讲,有两种状态:持有、未持有 股票。 -
则
i-1
到i
的话,共有四种状态: -
持有–》持有 无操作
-
未持有–》未持有 无操作
-
持有–》未持有:以当前价格卖出
-
未持有–》持有:以当前价格买入
-
借用一下灵神算法基础课第 21 课所讲:
-
设:
f[i][0]
表示持有股票,f[i][1]
表示未持有股票 -
初始化时有,
f[0][0] = -p[i]
,相当于第i
天买入了股票。f[0][0] = 0
相当于没股票,没操作。
贪心思路:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int res = 0;
for (int i = 1; i < prices.size(); ++i)
res += max(0, prices[i] - prices[i - 1]);
return res;
}
};
状态机dp 思路:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
int f[n][2]; memset(f, 0, sizeof(f));
f[0][0] = -prices[0];
f[0][1] = 0;
for (int i = 1; i < n; i ++ ) {
f[i][0] = max(f[i - 1][0], f[i - 1][1] - prices[i]);
f[i][1] = max(f[i - 1][1], f[i - 1][0] + prices[i]);
}
return max(f[n - 1][0], f[n - 1][1]);
}
};
2025年02月08日21:53:33
上面确实很容易理解,golang 版本如下:
func maxProfit(prices []int) int {
n := len(prices)
f := make([][]int, n)
for i := 0; i < n; i ++ {
f[i] = make([]int, 2)
}
f[0][0] = -prices[0]
f[0][1] = 0
for i := 1; i < n; i ++ {
f[i][0] = max(f[i - 1][0], f[i - 1][1] - prices[i])
f[i][1] = max(f[i - 1][1], f[i - 1][0] + prices[i])
}
return max(f[n - 1][0], f[n - 1][1])
}