代码随想录算法训练营day55 || 309.最佳买卖股票时机含冷冻期,714.买卖股票的最佳时机含手续费

动态规划来决定最佳时机,这次有冷冻期!| LeetCode:309.买卖股票的最佳时机含冷冻期_哔哩哔哩_bilibili

动态规划来决定最佳时机,这次含手续费!| LeetCode:714.买卖股票的最佳时机含手续费_哔哩哔哩_bilibili

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

思路:本题增加了一种冷冻期的量,引出一种全新的状态。我所理解的是本题增加了一个状态,是冷冻状态。代码随想录 对将本题细分为了四个状态。我个人理解上还是认为三个状态更加好理解,但是的确三个状态之中存在一个疑惑,这点我们一会儿来说明。

首先股票类题目可以总结出这样的几个前提:

  • dp[i][k] 表示的是当天之后处于什么样的状态且拥有怎样的最大收益,且每天结束时与后一天开始时是等价的;
  • 每一天的收益与状态是与依赖前一天所转化的,这不仅是与现实逻辑——要一天天过相通;同时也是保证每一次状态转移是贴合题目逻辑也是可以形成形式化的操作。所以第i天不会涉及到i-2天的内容,而i-2天的结果一定是传递到了i-1天的结果中,然后再传递到i天的结果中;

现在给出三个状态:0表示不持有股票;1表示持有股票;2表示处于冷冻期;

相应的dp[i][0],第i天结束后处于不持有股票状态且不处于冷冻期;dp[i][1]表示结束时处于持有股票状态;dp[i][2]表示今天结束之后处于冷冻期状态且不持有股票。三个状态的值之间转换是这样,注意我们所考虑的i一定是参考i-1及其之前的,比参考i的,所以第i天卖了股票后状态就变成2了,且后一天一定也是2,这一点三种状态的核心争议点的来源,明确这一点后三种状态就比较清晰了。

  • dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i],第i天结束后处于持有股票的状态;要么延续买了前一天有股票状态;要么可以买入然后买了股票,这其中包括前一天是0或者前两天是2;
  • dp[i][2] = dp[i-1][1]+prices[i],第i天结束之后处于冷冻状态,那么一定只有卖了股票才会出现冷冻期状态。此外这里0和2一定是独立分开的,所以如果卖了股票是处于冷冻期,那么一定不会转换到不是冷冻期的0,所以0状态与1状态之间没有关联的可能。并且冷冻期只会是一天,i-1是冷冻期,则第i天也是冷冻期。
  • dp[i][0] = max(dp[i-1][0], dp[i-1][2]) 第i天结束后处于不持有且不冷冻的状态,那么有两种来源,其一是买了股票持有,与不持有相悖,所以0不会从1状态转换过来;所以只会是延续前一天,或者前一天是卖出了股票或者是冷冻期,那么今天结束时就会恢复到可以购买,即后一天可以购买,那么也可以反过来等效为今天结束时冷冻期已经解冻了
// 时间复杂度O(n)
// 空间复杂度O(3*n)

class Solution {
    public int maxProfit(int[] prices) {
        // 具有三种状态
        // dp[i][0] 表示处于不持有股票的状态,且不处于冷冻期
        // dp[i][1] 表示持有股票
        // dp[i][2] 表示不持有股票且处于冷冻期
        int n = prices.length;
        int[][] dp = new int[n][3];

        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        dp[0][2] = 0;

        for(int i=1; i<n; i++){
            // dp[i][0/1/2]表示的都是第i天结束之后的最大收益

            // 持有股票,可以是第i-1天结束后就持有股票;也可以是i-1天结束后处于不持有股票且不处于冷冻期在第i天进行了股票买入
            if(i > 2)
                dp[i][1] = Math.max(dp[i-1][1], Math.max(dp[i-1][0]-prices[i], dp[i-2][2]-prices[i]));
            else
                dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0]-prices[i]);
            // 不持有股票且不是冷冻期,可以是第i-1天结束后是不持有股票且不处于冷冻期,第i天继续不持有;也可以是第i-1天结束后处于了冷冻期,则今天一天处于冷冻期,但是今天结束后就不是冷冻期
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][2]);
            // 处于冷冻期,一定是第i天卖出了股票,若第i-1天处于冷冻期,则第i天冷冻期已经结束了
            dp[i][2] = dp[i-1][1]+prices[i];
        }

        return Math.max(dp[n-1][0], dp[n-1][2]);
    }
}

如果使用三个状态一定要明确dp[i][k]表示的是当天结束时处于了什么状态,当天的操作已经全部完成了;第二当天结束时的状态是等效与后一天开始时的状态的,即后一天可以做什么操作,在当天结束时就可以做同样的所有操作(比如结束时是2,当前与后一天都是冷冻,但后一天的结束时就是0了,与后两天开始是0保持一致,后两天开始时可以做的所有操作在后一天结束时就已经可以做了;结束时是1,则后一天可以卖,也可以什么都不操作;结束时是0,则后一天可以买入,也可以什么都不操作)

另解

// 时间复杂度O(n)
// 空间复杂度O(2n+2)

class Solution {
    public int maxProfit(int[] prices) {
        // 买入股票的最佳时机II 的每一步卖出时加入处理手续费的操作即可
        int n = prices.length;

        // 扩展为n+1是为了更好的处理i-2的操作
        int[][] dp = new int[n+1][2];
        // 初始化,第0天拥有股票显然是不合法的
        dp[0][1] = Integer.MIN_VALUE;
        dp[0][0] = 0;

        // 注意这里的i是表示天数,而不是数组的索引下标,且初始化时没有使用任一数组内的元素,所以访问数组需要i-1
        for(int i=1; i<=n; i++){
            if(i>1)
                dp[i][1] = Math.max(dp[i-1][1], dp[i-2][0]-prices[i-1]);// 持有状态只会是延续前一天或者是来源于不算冷冻期的最近一次不持有状态,直接减去2,这其中依赖于递推一定是一步一步从头或从尾进行遍历的
            else
                dp[i][1] = Math.max(dp[i-1][1], -prices[i-1]);          // 第一天结束处于持有状态,并且更新为合法的值
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]+prices[i-1]);
        }
        return dp[n][0];
    }
}

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

思路:就是在 122.买卖股票的最佳时机II 中每次卖出股票时增加手续费的操作即可求解。

// 时间复杂度O(n)
// 空间复杂度O(2*n)

class Solution {
    public int maxProfit(int[] prices, int fee) {
        // 买入股票的最佳时机II 的每一步卖出时加入处理手续费的操作即可
        int n = prices.length;

        int[][] dp = new int[n][2];
        dp[0][1] = -prices[0];
        dp[0][0] = 0;

        for(int i=1; i<n; i++){
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0]-prices[i]);
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]+prices[i]-fee);
        }
        return dp[n-1][0];
    }
}

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值