当前位置:   article > 正文

代码随想录算法训练营day40:动态规划08:股票问题_给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。 设计一个算法计算

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。 设计一个算法计算

121. 买卖股票的最佳时机

力扣题目链接(opens new window)

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

  • 示例 1:

  • 输入:[7,1,5,3,6,4]

  • 输出:5
    解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

  • 示例 2:

  • 输入:prices = [7,6,4,3,1]

  • 输出:0
    解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

贪心

因为股票就买卖一次,那么贪心的想法很自然就是取最左最小值,取最右最大值,那么得到的差值就是最大利润。

我一开始写的 动态规划

还是比较基于贪心算法的方法

  1. int maxProfit(int* prices, int pricesSize) {
  2. int dp[pricesSize];//在第j天 及以前,能得到的最大收益
  3. memset(dp, 0, sizeof(dp));
  4. if(pricesSize==0 || pricesSize==1) return 0;
  5. dp[0]=0;
  6. dp[1]=fmax(0,prices[1]-prices[0]);
  7. int min=fmin(prices[0],prices[1]);//记录最小的成本
  8. for (int j=2;j<pricesSize;j++){
  9. if(min>prices[j]) {//应该在第j天卖出
  10. min=prices[j];
  11. dp[j]=dp[j-1];//成本价改变,但是在第j天卖出 最大收益没办法计算到第j-1天
  12. }
  13. else dp[j]=fmax(dp[j-1],prices[j]-min);
  14. }
  15. for (int j=0;j<pricesSize;j++){
  16. printf("%d",dp[j]);
  17. }
  18. return dp[pricesSize-1];
  19. }

动态规划:

确定dp数组(dp table)以及下标的含义

dp[i][0] 表示第i天持有股票所得最多现金,其实一开始现金是0,那么加入第i天买入股票现金就是 -prices[i], 这是一个负数。

dp[i][1] 表示第i天不持有股票所得最多现金

注意这里说的是“持有”,“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态

确定递推公式

如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来

  • 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
  • 第i天买入股票,所得现金就是买入今天的股票后所得现金即:-prices[i]

那么dp[i][0]应该选所得现金最大的,所以dp[i][0] = max(dp[i - 1][0], -prices[i]);

如果第i天不持有股票即dp[i][1], 也可以由两个状态推出来

  • 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
  • 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp[i - 1][0]

同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);

这样递推公式我们就分析完了

dp数组如何初始化

由递推公式 dp[i][0] = max(dp[i - 1][0], -prices[i]); 和 dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);可以看出

其基础都是要从dp[0][0]和dp[0][1]推导出来。

那么dp[0][0]表示第0天持有股票,此时的持有股票就一定是买入股票了,因为不可能有前一天推出来,所以dp[0][0] -= prices[0];

dp[0][1]表示第0天不持有股票,不持有股票那么现金就是0,所以dp[0][1] = 0;

确定遍历顺序

从递推公式可以看出dp[i]都是由dp[i - 1]推导出来的,那么一定是从前向后遍历。

  1. int maxProfit(int* prices, int pricesSize) {
  2. int dp[pricesSize][2];//第几天,0表示第j天不持有股票(包括j天卖掉),1表示持有股票(包括j天买入)
  3. memset(dp, 0, sizeof(dp));
  4. dp[0][0]=0;
  5. dp[0][1]=-prices[0];
  6. for (int j=1;j<pricesSize;j++){
  7. dp[j][0]=fmax(dp[j-1][0],prices[j] + dp[j-1][1]);
  8. dp[j][1]=fmax(dp[j-1][1],-prices[j]);
  9. }
  10. return fmax(dp[pricesSize-1][0],dp[pricesSize-1][1]);
  11. }

122.买卖股票的最佳时机II

力扣题目链接(opens new window)

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

  • 示例 1:

  • 输入: [7,1,5,3,6,4]

  • 输出: 7
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

  1. int maxProfit(int* prices, int pricesSize) {
  2. int dp[pricesSize][2];
  3. //0表示这天手上没有股票,包括这天卖出,最多手上有多少钱
  4. //1表示这天手上有股票,包括这天买到,最多手上有多少钱
  5. memset(dp, 0, sizeof(dp));
  6. dp[0][0]=0;
  7. dp[0][1]=-prices[0];
  8. for(int j=1;j<pricesSize;j++){
  9. //第j天最后手上没有股票的两种情况:本来就没有,卖掉了
  10. dp[j][0]= fmax(dp[j-1][0], prices[j] + dp[j-1][1]);
  11. //第j天手上最后有股票的两种情况:本来就有,今天刚买了股票(买股票要求本来手上没有股票)
  12. dp[j][1]= fmax(dp[j-1][1], dp[j-1][0]-prices[j]);
  13. }
  14. return fmax(dp[pricesSize-1][0], dp[pricesSize-1][1]);
  15. }

123.买卖股票的最佳时机III

力扣题目链接(opens new window)

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

  • 示例 1:

  • 输入:prices = [3,3,5,0,0,3,1,4]

  • 输出:6 解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3。

分析:

按照题意,区分每一天的状态!

然后会发现无操作的状态可以不用考虑

重点是初始化!!!为什么day0 第三个状态需要初始化呢?:dp【i】【j】记录的是在第i天 j种情况中花费掉的钱,需要算在里面,后续计算的dp【i】【3】会用到

为什么最后一个的k情况是最大的:现在最大的时候一定是卖出的状态,而两次卖出的状态现金最大一定是最后一次卖出。如果想不明白的录友也可以这么理解:如果第一次卖出已经是最大值了,那么我们可以在当天立刻买入再立刻卖出。所以dp[4][4]已经包含了dp[4][2]的情况。也就是说第二次卖出手里所剩的钱一定是最多的。

最后一单未进行的情况,或者说可以理解成原地出售的情况,其实暗含在dp[i][4]=fmax(dp[i-1][4]中了

  1. int maxProfit(int* prices, int pricesSize) {
  2. int dp[pricesSize][5];//没有操作 0 ,能拿到的最大的钱
  3. //第一次持有股票 1
  4. //第一次不持有股票 2
  5. //第二次持有股票 3
  6. //第二次不持有股票 4
  7. memset(dp,0,sizeof(dp));
  8. dp[0][1]=-prices[0];
  9. dp[0][3]=-prices[0];//这个初始化很重要!!!
  10. for (int i=1;i<pricesSize;i++){
  11. //dp[i][0]=dp[i-1]
  12. dp[i][1]=fmax(dp[i-1][1],-prices[i]);//分为本来就有股票,和今天买入的股票
  13. dp[i][2]=fmax(dp[i-1][2],prices[i] + dp[i-1][1]);
  14. dp[i][3]=fmax(dp[i-1][3], dp[i-1][2]-prices[i]);
  15. dp[i][4]=fmax(dp[i-1][4],prices[i]+dp[i-1][3]);
  16. }
  17. return dp[pricesSize-1][4];
  18. }

188.买卖股票的最佳时机IV

力扣题目链接(opens new window)

给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

  • 示例 1:

  • 输入:k = 2, prices = [2,4,1]

  • 输出:2 解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2。

  • 示例 2:

  • 输入:k = 2, prices = [3,2,6,5,0,3]

  • 输出:7 解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4。随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。

  1. int maxProfit(int k, int* prices, int pricesSize) {
  2. int total=2*k;
  3. int dp[pricesSize][total];
  4. //1,3,5,7,9...这些是卖出的
  5. //0是第一次有股票,1对应第一次卖掉
  6. memset(dp,0,sizeof(dp));
  7. for (int i=0;i<k;i++){
  8. dp[0][2*i]=-prices[0];
  9. }
  10. for (int t=1;t<pricesSize;t++){
  11. dp[t][0]=fmax(dp[t-1][0],-prices[t]);///一开始这个赋值没有想清楚
  12. dp[t][1]=fmax(dp[t-1][1] , prices[t]+dp[t-1][0]);
  13. for (int i=1;i<k;i++){
  14. //第i次买到股票,本来手上就有 && 今天刚买
  15. dp[t][2*i]= fmax(dp[t-1][2*i], dp[t-1][2*i-1]-prices[t] );
  16. //第i次卖掉股票,本来就没有or卖掉了
  17. dp[t][2*i+1]=fmax(dp[t-1][2*i+1] , prices[t]+dp[t-1][2*i]);
  18. }
  19. }
  20. return dp[pricesSize-1][2*k-1];
  21. }

309.最佳买卖股票时机含冷冻期

力扣题目链接(opens new window)

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
  • 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

示例:

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

分析:

具体可以区分出如下四个状态:——状态要尽量分的细致一点!!!只考虑昨天 对 今天的影响

  • 状态一:持有股票状态(今天买入股票,或者是之前就买入了股票然后没有操作,一直持有)
  • 不持有股票状态,这里就有两种卖出股票状态
    • 状态二:保持卖出股票的状态(两天前就卖出了股票,度过一天冷冻期。或者是前一天就是卖出股票状态,一直没操作)
    • 状态三:今天卖出股票
  • 状态四:今天为冷冻期状态,但冷冻期状态不可持续,只有一天!

  1. int maxProfit(int* prices, int pricesSize) {
  2. //0:手上有股票——手上有最多的钱是多少?
  3. //1:手上没有股票,且不是今天卖出的股票
  4. //2:今天卖出的股票
  5. //3:在冷冻期
  6. int dp[pricesSize+1][4];
  7. memset(dp,0,sizeof(dp));
  8. dp[0][0]=-prices[0];
  9. for(int j=1;j<pricesSize;j++){
  10. dp[j][0]=fmax(dp[j-1][0], fmax(dp[j-1][1]-prices[j] , dp[j-1][3] -prices[j] ) );
  11. dp[j][1]= fmax(dp[j-1][1], fmax(dp[j-1][2],dp[j-1][3]) );
  12. dp[j][2]= dp[j-1][0] +prices[j];
  13. dp[j][3] = dp[j-1][2];
  14. }
  15. return fmax(dp[pricesSize-1][1],fmax(dp[pricesSize-1][2],dp[pricesSize-1][3]));
  16. }

714.买卖股票的最佳时机含手续费

力扣题目链接(opens new window)

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

示例 1:

  • 输入: prices = [1, 3, 2, 8, 4, 9], fee = 2
  • 输出: 8

只要在122的基础上,加上卖股票的时候扣去手续费即可!

  1. int maxProfit(int* prices, int pricesSize, int fee) {
  2. int dp[pricesSize][2];
  3. //0表示这天手上没有股票,包括这天卖出,最多手上有多少钱
  4. //1表示这天手上有股票,包括这天买到,最多手上有多少钱
  5. memset(dp, 0, sizeof(dp));
  6. dp[0][0]=0;
  7. dp[0][1]=-prices[0];
  8. for(int j=1;j<pricesSize;j++){
  9. //第j天最后手上没有股票的两种情况:本来就没有,卖掉了
  10. dp[j][0]= fmax(dp[j-1][0], prices[j] + dp[j-1][1]-fee);//就是这里!!!!!!!!!!!!!!!!!
  11. //第j天手上最后有股票的两种情况:本来就有,今天刚买了股票(买股票要求本来手上没有股票)
  12. dp[j][1]= fmax(dp[j-1][1], dp[j-1][0]-prices[j]);
  13. }
  14. return fmax(dp[pricesSize-1][0], dp[pricesSize-1][1]);
  15. }

Leetcode股票问题总结篇!

之前我们已经把力扣上股票系列的题目都讲过的,但没有来一篇股票总结,来帮大家高屋建瓴,所以总结篇这就来了!

股票问题总结

贪心思路:

最左最小值,最右最大值

复杂一些之后需要使用动态规划的思路:

将每天股票状态分成若类,每一类都可以用前一天的状态决定

讨论每天每种状态的最大值

注意递推公式细节和初始化

有时候会写错dp和nums数组,还有要确保下标有效 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/1020058
推荐阅读
相关标签
  

闽ICP备14008679号