每日一题 2866. 美丽塔 II(中等,单调栈)

在这里插入图片描述

  1. 理解题目意思,希望构建一个数组,该数组的最大值的两边依次单调递减,形成一个峰值,且该数组每个下标对应的值的大小是有上限的
  2. 显然可以分为两部分求解,因为最大值的左半部分和右半部分是不相干的,因此我们只要找到所有下标的左右两半的最大值,再求和找总的最大值即可,而左右两半的求解方式显然是类似的,下面解释左半最大值的解法
  3. 抽象法,如下图所示,构建一个以下标所能形成的最大值为值的数组,我们可以清楚地看到如果以下标 7 为峰值,那么不管 6 之前的值有多大,所有的值都只能取到 6 对应的值。或者说在 6 之前的值就只与 6 的峰值有关了,根 7 没有关系了;如果以下标 5 为峰值,那么它的左边一开始是比它大的,这些都能够取到 5 的峰值,然后直到下标 2 比 5 的峰值小,接下来与 5 也无关了总结;总之,当以下标 i 为峰值时,先寻找它左边的最长比它大序列,这些都是取 i 的峰值的,在这之后的取值就与 i 无关了,应当是当以下标 t 为峰值时(下标 t 则是第一个比 i 的峰值小的下标),左半部分的取值和的最大值
  4. 右半部分同理可求,代码实现需要对每一个 i 寻找它两边的最长比它大序列,然后把两边加起来求最大值,如果最长比它大序列采用遍历的方式,其时间复杂度为O(n2),现在我们需要找到一个更好的来最长比它大序列的方法
  5. 由步骤 3 我们可以知道,例如下标 6,不管1,2,3,4,5对应的峰值有多大,都无法影响到之后的下标的取值,因此采用单调栈的思想,当下标 6 入栈时,弹出所有比 6 的峰值更大的下标,这些下标都被 6 的峰值限制了;再看到对于下标 5 而言,弹出 3,4之后,遇到栈中第一个比它小的下标 2,此时就和步骤3的结论一样,下标 5 对应的左半部分的最大值就是弹出栈的元素的数量乘以 5 的峰值,再加上此时栈顶元素对应左半部分最大值
  6. 由步骤 5 我们可以以总体 O(n) 的时间复杂度来求解找到一个更好的来最长比它大序列,因为每个下标智慧出栈入栈一次
  7. 最后右半部分同样的处理,然后求和取最大值求解
    在这里插入图片描述
class Solution:
    def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
        n = len(maxHeights)
        leftm, rightm = [0] * n, [0] * n
        leftm[0] = maxHeights[0]
        rightm[n - 1] = maxHeights[n - 1]
        st = [0]
        for i in range(1, n):
            while len(st) > 0 and maxHeights[i] < maxHeights[st[-1]]:
                st.pop()
            leftm[i] = (leftm[st[-1]]  + maxHeights[i] * (i - st[-1])) if len(st) > 0 else maxHeights[i] * (i + 1) 
            st.append(i)
        st = [n - 1]
        for i in range(n - 2, -1, -1):
            while len(st) > 0 and maxHeights[i] <= maxHeights[st[-1]]:
                st.pop()
            rightm[i] = (rightm[st[-1]]  + maxHeights[i] * (st[-1] - i)) if len(st) > 0 else maxHeights[i] * (n - i) 
            st.append(i)

        return max([rightm[i] + leftm[i] - maxHeights[i] for i in range(n)])
  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

eyvr

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

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

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

打赏作者

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

抵扣说明:

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

余额充值