力扣刷题笔记 309. 最佳买卖股票时机含冷冻期 C#

今日签到题,题目如下:

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

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

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

今天的提交记录是这样的:

力扣刷题笔记 309. 最佳买卖股票时机含冷冻期 C#_第1张图片

有点绝望,得亏我看到题目就想到要用动态规划。

我的思路大概是这样的,使用长度为 n 的 int 数组 dp记录状态,然后逆序遍历 prices ,在其中用一大堆乱七八糟的条件进行状态转移。虽然中间考虑过存储买入、卖出、冻结等等不同状态,但是没想好怎么存,感觉是每个 dp[i] 开辟一个 i 长度的数组记录这些状态,所以就没有使用。没有得出正确题解,具体思路就不叙述了。

好吧,还是看官方题解,存储不同状态是没有错的,但是并不是存储很长的一个最优解状态,而是使用长度为 n*3 的二维数组 dp,dp[i,0] 表示 i 天之后处于持股状态(可能是第 i 天买入,也可能是第 i 天之前的某一天买入),dp[i,1] 表示 i 天之后处于冻结状态(即第 i 天卖出,只可能是这种情况),dp[i,2] 表示 i 天之后即不持股也不冻结(即第 i 天是冻结后的第一天之后的某一天)

看了题解之后我先是自己分了一次状态,我分状态的方式就是当天买入、卖出、冻结,但是实际上,这样不包括没有任何操作的状态,但是实际上没有任何操作的状态又分为持股和不持股的两种状态。上文中官方题解的方法则包含了所有状态。

然后讨论状态转移:

  • dp[i,0],第 i 天之后为持股状态,两种可能的情况:
  1. 如果是第 i 天买入,则前一天为非持股非冻结状态,即状态转移自 dp[i-1,2],又有第 i 天买入,所以利润减去当天价格即 prices[i],结果表达为 dp[i-1,2] - prices[i]。
  2. 如果是第 i 天之前的某一天买入,则前一天也为持股状态,即状态转移自 dp[i-1,0],因为已经持股了,而且不卖出,即当天没有任何操作,结果表达式就是 dp[i-1,0]。

需要求最大利润,所以 dp[i,0] 取两个可能的较大值,表达式即为 dp[i,0] = Math.Max(dp[i - 1,0],dp[i-1,2] - prices[i])。

  • dp[i,1],第 i 天之后为冻结状态,此时只有当天是卖出才能出现第二天是冻结的情况。因为当天卖出,所以前一天必须持股,状态转移自 dp[i-1,0],还需要加上当天卖出的收益即当天的价格,结果表达式为 dp[i,1] = prices[i] + dp[i - 1,0]。其实这里我一开始写成了 prices[i] - dp[i - 1,0],鬼知道我当时在想什么。
  • dp[i,2],第 i 天之后为非持股非冻结状态,两种可能的情况:
  1. 冻结后的第一天,那么前一天就是冻结状态,所以状态转移自 dp[i-1,1],没有任何操作。
  2. 冻结后的第二天开始的某一天,那么前一天也是非持股非冻结状态,所以状态转移自 dp[i-1,2],没有任何操作。

最后确认边界条件,因为所有的状态转移自前一天的状态,所以只需要确定 dp[0,n] 的三个状态

  • dp[0,0],因为是第一天,所以只能是当天买入,dp[0,0] = -prices[0]。
  • dp[0,1],不持股,但是当天又是卖出操作,大概算是无效交易吧,所以利润还是 0,dp[0,0] = 0。
  • dp[0,2],因为没有操作,所以 dp[0,0] = 0。

复杂度分析:

只需要遍历一次数组,所以时间复杂度为 O(N)。需要一个 n*3 的二维数组保存状态,所以空间复杂度为 O(N)。题解中有提到因为只需要状态都转移自 i - 1,所以实际可以只使用三个变量不断地更新状态,空间复杂度降至 O(1),我自己没有实现就是了。

以下为自己提交的代码:

public class Solution {
    public int MaxProfit(int[] prices) {
        if (prices.Length == 0)
        {
            return 0;
        }
        int[,] dp = new int[prices.Length,3];
        // 0 为第二天持股(包括买入和持有),1为第二天冻结,2为第二天不持股期且不冻结
        dp[0,0] = -prices[0];
        dp[0,1] = 0;
        dp[0,2] = 0;
        for (int i = 1;i

 

你可能感兴趣的:(基础算法)