2865. 美丽塔 I:使用原理图理解单调栈思想

2865. 美丽塔 I:使用原理图理解单调栈思想

题目描述

2865. 美丽塔 I - 力扣(LeetCode)

给你一个长度为 n 下标从 0 开始的整数数组 maxHeights
你的任务是在坐标轴上建 n 座塔。第 i 座塔的下标为 i ,高度为 heights[i]
如果以下条件满足,我们称这些塔是 美丽 的:

  1. 1 <= heights[i] <= maxHeights[i]
  2. heights 是一个 山脉 数组。
    如果存在下标 i 满足以下条件,那么我们称数组 heights 是一个 山脉 数组:
  • 对于所有 0 < j <= i ,都有 heights[j - 1] <= heights[j]
  • 对于所有 i <= k < n - 1 ,都有 heights[k + 1] <= heights[k]

 
请你返回满足 美丽塔 要求的方案中,高度和的最大值
 
示例 1:
输入: maxHeights = [5,3,4,1,1]
输出: 13
解释: 和最大的美丽塔方案为 heights = [5,3,3,1,1] ,这是一个美丽塔方案,因为:

  • 1 <= heights[i] <= maxHeights[i]
  • heights 是个山脉数组,峰值在 i = 0 处。
    13 是所有美丽塔方案中的最大高度和。

题目解读

  1. 什么是美丽塔: 只有一个峰值。
  2. 举例:设给定maxHeights = [5,3,4,1,1],则可以获取如下美丽塔。
    1. 峰值在下标为0处的美丽塔为 [5,3,4,1,1], 峰值为 5
    2. 峰值在下标为1处的美丽塔为 [3,3,3,1,1], 峰值为 3
    3. 峰值在下标为2处的美丽塔为 [3,3,4,1,1], 峰值为 4
    4. 峰值在下标为3处的美丽塔为 [1,1,1,1,1], 峰值为 1
    5. 峰值在下标为4处的美丽塔为 [1,1,1,1,1], 峰值为 1
    6. 即峰值在下标为i处的美丽塔满足两个条件,其左边非递减,右边非递增关系。
      1. 左边非递减:即j∈[1,i]时,均满足 heights[j−1]<=heights[j]
      2. 右边非递增:即j∈[i,n−2],均满足 heights[j]>=heights[j+1]
  3. 然后获取包含最大高度和的美丽塔。
  4. 问题首先就变成了怎么求美丽塔的高度和。
  5. 这种限制条件为递增或递减的问题可以考虑单调栈

求非递减

非递减原理图

  1. x轴表示下标,黑色折现图表示允许建造的最大高度。红色折线图表示峰值在下标为7处的美丽塔高度情况。
  2. 可以看到,如果本来就是非递减的,那么保持原样,如果出现递减的情况了会将非递减的部分变成红色的相等部分。非递减原理如下图非递减原理图

单调栈的应用

  1. 单调栈中存储的是单调高度对应的数据下标。比如我们要求非递减部分,就可以使用一个非递减单调栈存储值。
  2. 当要处理的元素k小于单调栈的元素了,那么就将栈中大于k的元素依次出栈,始终保持单调状态。
    while (!stack1.empty() && maxHeights[i] < maxHeights[stack1.top()]) {
                stack1.pop();
           }
  1. 设以下标i处的值为峰值的左边非递减部分和为prefix[i],右边非递增部分和为suffix[i]

  2. prefix[3],我们通过非递减单调栈找到最近的比较小的元素下标即1(这时候大于maxHeights[3]的值就不存在了)则prefix[3] = prefix[1] + (3-1) * maxHeights[3]prefix[3]计算原理图如下:
    prefix[3]计算原理图

  3. 这样遍历从0开始正序遍历所有元素就可以求出prefix[i]

  4. 同理逆序遍历所有元素就可以求出所有的suffix[i]

代码


long long maximumSumOfHeights(vector<int>& maxHeights) {

        int n = maxHeights.size();
        long long res = 0;
        vector<long long> prefix(n), suffix(n);
        //stack1 表示非递减栈,stack2表示非递增栈。
        stack<int> stack1, stack2;
        
        //求prefix
        for (int i = 0; i < n; i++) {
            while (!stack1.empty() && maxHeights[i]< maxHeights[stack1.top()]) {
                stack1.pop();
            }
            if (stack1.empty()) {
                cout<<i<<endl;
                prefix[i] = (long long)(i + 1) * maxHeights[i];
            } else {
                cout<<i<<" "<<stack1.top()<<endl;
                prefix[i] = prefix[stack1.top()] + (long long)(i - stack1.top()) * maxHeights[i];
            }
            stack1.emplace(i);      
        }
        // 求suffix同时获取结果。
        for (int i = n - 1; i >= 0; i--) {
            while (!stack2.empty() && maxHeights[i] < maxHeights[stack2.top()]) {
                stack2.pop();
            }
            if (stack2.empty()) {
                suffix[i] = (long long)(n - i) * maxHeights[i];
            } else {
                suffix[i] = suffix[stack2.top()] + (long long)(stack2.top() - i) * maxHeights[i];
            }
            stack2.emplace(i);
            res = max(res, prefix[i] + suffix[i] - maxHeights[i]);
        }
        return res;
    }
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值