算法---LeetCode 309. 最佳买卖股票时机含冷冻期

1. 题目

原题链接

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

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

示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
Related Topics 动态规划
👍 786 👎 0

2. 题解

2.1 解法1: 动态规划

「买入」为负收益,而「卖出」为正收益。在初入股市时,你只有「买入」的权利,只能获得负收益
「处于冷冻期」指的是在第 i 天结束之后的状态。也就是说:如果第 i 天结束之后处于冷冻期,那么第 i+1 天无法买入股票。

  1. 状态数组: dp[i][0-2],
    dp[i][0] 代表第 i 天持有股票的累计最大收益,
    dp[i][1] 代表第 i 天不持有股票且在冷冻期内的累计最大收益
    dp[i][2] 代表第 i 天不持有股票且不在冷冻期内的累计最大收益
  2. 递推公式:
  • dp[i][0], 可能有两种情况,
    a. 前一天就持有股票即 dp[i-1][0]
    b. 前一天不持有股票, 且不处于冷冻期中, 此时dp[i][0]=dp[i-1][1]-price[i]
  • dp[i][1]
    a. 说明当天卖出了股票, 前一天持有股票, 有 dp[i][1]=dp[i-1][0]+price[i]
  • dp[i][2]
    a. 前一天不持有股票且不在冷冻期, dp[i][2]
    b. 前一天不持有股票且在冷冻期, dp[i][1]
  1. 初始化: dp[0][0]=-price[i], dp[0][1]=0, dp[0][2]=0
   class Solution {
       public int maxProfit(int[] prices) {
           if (prices.length == 0) {
               return 0;
           }
           int n = prices.length;
           int[][] dp = new int[n][3];
           dp[0][0] = -prices[0];
           for (int i = 1; i < n; i++) {
               dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
               dp[i][1] = dp[i - 1][0] + prices[i];
               dp[i][2] = Math.max(dp[i - 1][1], dp[i - 1][2]);
           }
           return Math.max(dp[n - 1][1], dp[n - 1][2]);
       }
   }
空间优化: 由于递推过程中, dp[i][…] 只使用了前一天的dp[i-1][…], 所以可以使用三个变量来滚动
    class Solution {
        public int maxProfit(int[] prices) {
            if (prices.length == 0) {
                return 0;
            }
            int n = prices.length;
            int f0 = -prices[0];
            int f1 = 0;
            int f2 = 0;
            for (int i = 1; i < n; i++) {
                int newF0 = Math.max(f0, f2 - prices[i]);
                int newF1 = f0 + prices[i];
                int newF2 = Math.max(f1, f2);
                f0 = newF0;
                f1 = newF1;
                f2 = newF2;
            }
            return Math.max(f1, f2);
        }
    }

参考:
官方题解

2.2 写法2

转移方程

每次 sell 之后要等一天才能继续交易。只要把这个特点融入上一题的状态转移方程即可:

dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i])
解释:第 i 天选择 buy 的时候,要从 i-2 的状态转移,而不是 i-1 。

初始化

初始化第 0 天的状态
dp[0][0] = 0
dp[0][1] = -prices[0]

代码:

这个注意需要额外定义一个用于表示 dp[i-2][0] 的变量, 初始时值也为 0 , 具体在循环体内的赋值, 可以先弄清楚各个变量的含义, 然后在草稿纸上演算一下, 是否代表其正确的值, 避免出错

    class Solution {
        public int maxProfit(int[] prices) {
            if (prices.length == 0) {
                return 0;
            }
            // 初始化第 0 天的情况
            int dp_i_0 = 0;
            int dp_i_1 = -prices[0];
            // 代表 dp[i-2][0], 初始时为 0
            int dp_pre_0 = 0;
            for (int i = 1; i < prices.length; i++) {
                int temp = dp_i_0;
                dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i]);
                dp_i_1 = Math.max(dp_i_1, dp_pre_0 - prices[i]);
                dp_pre_0 = temp;
            }
            return dp_i_0;
        }
    }

参考:
算法—LeetCode 188. 买卖股票的最佳时机 IV(股票买卖问题总结)
团灭 LeetCode 股票买卖问题—labuladong的算法小抄

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值