【动态规划--买卖股票的最佳时期系列2】

力扣题目链接
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
实例

输入:[1,2,3,0,2]
输出:3
解释:对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

动态规划5步曲

  • 确定dp数组以及下标的含义
    dp[i][j],第i天状态为j,所剩的最多现金为dp[i][j]。
    本题的状态相对于系列一复杂了不少。具体分了如下四个状态:
 - 状态一:买入股票状态(今天买入股票,或者是之前就买入了股票然后没有操作)
 
    卖出股票状态,这里就有两种卖出股票状态
    
 - 状态二:两天前就卖出了股票,度过了冷冻期,一直没操作,今天保持卖出股票状态
 - 状态三:今天卖出了股票

 - 状态四:今天为冷冻期状态,但冷冻期状态不可持续,只有一天!

注意这里的每一个状态,例如状态一,是买入股票状态并不是说今天已经就买入股票,而是说保存买入股票的状态即:可能是前几天买入的,之后一直没操作,所以保持买入股票的状态。

j的状态为:
0:状态一;1:状态二;2:状态三;3:状态四

确定递推式
达到买入股票状态(状态一)即:dp[i][0],有两个具体操作:

操作一:前一天就是持有股票状态(状态一),dp[i][0] = dp[i - 1][0]
操作二:今天买入了,有两种情况
前一天是冷冻期(状态四),dp[i - 1][3] - prices[i]
前一天是保持卖出股票状态(状态二),dp[i - 1][1] - prices[i]
所以操作二取最大值,即:max(dp[i - 1][3], dp[i - 1][1]) - prices[i]
那么dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]);

达到保持卖出股票状态(状态二)即:dp[i][1],有两个具体操作:

操作一:前一天就是状态二
操作二:前一天是冷冻期(状态四)
dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);

达到今天就卖出股票状态(状态三),即:dp[i][2] ,只有一个操作:

操作一:昨天一定是买入股票状态(状态一),今天卖出
即:dp[i][2] = dp[i - 1][0] + prices[i];

达到冷冻期状态(状态四),即:dp[i][3],只有一个操作:

操作一:昨天卖出了股票(状态三)
dp[i][3] = dp[i - 1][2];

综上分析得递推公式:

dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
dp[i][2] = dp[i - 1][0] + prices[i];
dp[i][3] = dp[i - 1][2];

dp数组如何初始化
这里主要讨论一下第0天如何初始化。
如果是持有股票状态(状态一)那么:dp[0][0] = -prices[0],买入股票所剩现金为负数。
保持卖出股票状态(状态二),第0天没有卖出dp[0][1]初始化为0就行。
今天卖出了股票(状态三),同样dp[0][2]初始化为0,因为最少收益就是0,绝不会是负数。
同理dp[0][3]也初始为0。
确定遍历顺序
从递归公式上可以看出,dp[i] 依赖于 dp[i-1],所以是从前向后遍历。
举例推导dp数组
以 [1,2,3,0,2] 为例,dp数组如下:
在这里插入图片描述
最后结果是取 状态二,状态三,和状态四的最大值。
代码如下:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Solution{
	public:
		int maxProfit(vector<int> &prices) {
			int n = prices.size();
			if (n == 0) return 0;
			vector<vector<int>> dp(n,vector<int>(4,0));
			dp[0][0] = -prices[0];
			for (int i = 1; i < n; i++) {
				dp[i][0] = max({dp[i-1][0],dp[i-1][1]-prices[i],dp[i-1][3]-prices[i]});
                //买入股票状态
				dp[i][1] = max(dp[i-1][1],dp[i-1][3]);
                //两天前就卖出股票,度过了冷冻期,一直没有操作,今天保持卖出股票的状态
				dp[i][2] = dp[i-1][0] + prices[i];
                //今天卖出了股票
				dp[i][3] = dp[i-1][2];
                //今天为冷冻期,但冷冻期状态不可持续,只有一天
			}
			return max({dp[n-1][3],dp[n-1][2],dp[n-1][1]});
		}
}; 
int main() {
	Solution Q;
	int n;
	cin>>n;
	vector<int> price(n);
	for (int i = 0; i < n; i++) {
		cin>>price[i];
	}
	cout<<Q.maxProfit(price);
} 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
动态规划可以用来实现买卖股票的最佳时间。下面是一个动态规划的实现方法: 1. 定义状态: - `dp[i][j][k]` 表示第 i 天,进行了 j 次交易,持有状态为 k(0 表示未持有,1 表示持有)时的最大利润。 2. 初始化状态: - `dp[j] = 0`,第 0 天未进行交易且未持有股票时的利润为 0。 - `dp[j] = -prices`,第 0 天未进行交易但持有股票时的利润为负的股票价格。 3. 状态转移方程: - `dp[i][j] = max(dp[i-1][j][0], dp[i-1][j] + prices[i])`,第 i 天未持有股票的最大利润等于前一天未持有股票的最大利润和前一天持有股票但在第 i 天卖出的最大利润的较大值。 - `dp[i][j] = max(dp[i-1][j], dp[i-1][j-1] - prices[i])`,第 i 天持有股票的最大利润等于前一天持有股票的最大利润和前一天未持有股票但在第 i 天买入的最大利润的较大值。 4. 最终结果: - 最大利润为 `dp[n-1][k]`,其中 n 是天数,k 是最大交易次数。 下面是一个示例代码: ```python def maxProfit(prices, k): n = len(prices) if k > n // 2: # 如果 k 大于 n 的一半,相当于可以进行任意多次交易 return maxProfitUnlimited(prices) dp = [[[0] * 2 for _ in range(k+1)] for _ in range(n)] for i in range(n): for j in range(k, 0, -1): if i == 0: dp[i][j][0] = 0 dp[i][j][1] = -prices[i] else: dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]) dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]) return dp[n-1][k][0] def maxProfitUnlimited(prices): n = len(prices) dp = [[0] * 2 for _ in range(n)] for i in range(n): if i == 0: dp[i][0] = 0 dp[i][1] = -prices[i] else: dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]) return dp[n-1][0] ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-Gaojs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值