【LeetCode】2865. 美丽塔 I + 2866. 美丽塔 II

96 阅读5分钟

2865. 美丽塔 I

题目链接

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

在 第 i 处建塔的高度 heights[i] 必须在 [1, maxHeight[i]] 之间 题目给了 maxHeight[i] 需要最大值,因此可以维护一个单调栈,令 heights[i] = maxHeight[i], 而 i 之前的 数为 单调栈中比 maxHeight[i] 小些的数。

以 示例3 为例: maxHeights = [3,2,5,5,2,3]
总体原则: 只能有一个峰头 左右改 峰位置左边非严格递增, 右边非严格递减

3 : heights = [3]
2: 比栈顶3 小,由于要满足 由于 满足 heights[i] <= maxHeights[i], heights = [2, 2]

5: 5 比 2 大, heights = [2, 2, 5]

5: heights = [2, 2, 5, 5] 可见 要记录 最大值。

2: 比 5 小 heights = [2, 2, 5, 5, 2] ,

3: 比 2 大,但又不大于 5,无法成为新的峰位置。而 在这里插入图片描述 峰位置的右侧只能非严格递减。所以只能为 2

代码实现的时候发现一个问题,要是前面有一个峰值 a ,后面又碰到 一次 a , 同样是这个峰值,那到底 这个 a 在 前面的收益高 还是在 后面的位置收益高,所以应该每个 最高值 a 都要计算相应的 峰值。

  • 这个还是有问题 比如 这个示例, 用 次大值 结果更大 在这里插入图片描述

Python3

方法1: 枚举 每个元素作为山顶 O(n2)O(1)\lgroup O(n^2)、O(1) \rgroup

易于理解 参考链接

class Solution:
    def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
        res = 0
        n = len(maxHeights)
        for i in range(n):
            curSum = maxHeights[i]
            
            ## 处理 峰值位置 之后的数据
            pre = maxHeights[i]
            for j in range(i + 1, n):  # 从左到右  不严格递减   
                pre = min(pre, maxHeights[j])
                curSum += pre

            ## 处理 峰值位置 之前的数据
            pre = maxHeights[i]
            for j in range(i-1, -1, -1):  # 从右到左  不严格递减  
                pre = min(pre, maxHeights[j])
                curSum += pre

            res = max(res, curSum)
        return res

模拟 参考链接

看到这个 先创建 heights 列表再 求和的方法, 内存加大。

⭐ 方法2: 前后缀分解 + 单调栈 O(n)\lgroup O(n) \rgroup 【空间换时间】

参考链接

class Solution:
    def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
        pre, suf, n = [0], [0], len(maxHeights)
        stack_pre, stack_suf = [-1], [n]  # 求 前后缀 时 分别维护一个单调递增 栈

        for i in range(n):
            while len(stack_pre) > 1 and maxHeights[stack_pre[-1]] > maxHeights[i]: # 大于 1 是 因为 加了  哨兵
                stack_pre.pop()   # 去掉 比 当前峰顶  大的
            pre.append(pre[stack_pre[-1] + 1] + maxHeights[i] * (i - stack_pre[-1]))  # 0 1 2 3   # 前面的前缀和 可以利用
            stack_pre.append(i)  ## 存储的是 下标

            j = n - 1- i 
            while len(stack_suf) > 1 and maxHeights[stack_suf[-1]] > maxHeights[j]:
                stack_suf.pop()

            suf.append(suf[n-stack_suf[-1]] + maxHeights[j] * (stack_suf[-1]-j))
            stack_suf.append(j)

        return max([ pre[i]+suf[n-i] for i in range(n) ])

参考链接1 参考链接2 在这里插入图片描述

在这里插入图片描述

相当于 利用 之前处理过的部分

class Solution:
    def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
        n = len(maxHeights)
        suf = [0] * (n + 1)  ## 存储 以 maxHeight[i] 为 峰 时 右侧的后缀和
        pre = [0] * (n + 1)   ## 存储 以 maxHeight[i] 为 峰 时 左侧的前缀和
        res = 0
        
        # 求 Pre  前缀
        stack = []  # 维护 一个单调增的栈  始终是栈顶为 峰  !!!存储的是  下标 只需要知道值  
        for i in range(n): # 
            while stack and maxHeights[i] < maxHeights[stack[-1]]: # 当前元素 小于 栈顶了, 要修改栈中元素  遍历栈中 当前元素
                stack.pop()  # 记录 弹出的最终值的下标
            j = stack[-1] if stack else -1
            pre[i+1] = pre[j+1] + (i - j) * maxHeights[i]
            stack.append(i)

        # 求 suf  后缀 
        stack = []
        for i in range(n-1, -1, -1): # 注意 suf 总长为 n + 1
            while stack and maxHeights[i] < maxHeights[stack[-1]]: # 当前元素 小于 栈顶了, 要修改栈中元素  遍历栈中 当前元素
                stack.pop()  # 记录 弹出的最终值的下标
            j = stack[-1] if stack else n
            suf[i] = suf[j] + (j - i) * maxHeights[i] 
            stack.append(i)
            res = max(res, pre[i+1] + suf[i] - maxHeights[i])
        return res 

复杂度分析:

  • 时间复杂度: O(n)O(n)。单层循环。
  • 空间复杂度: O(n)O(n)。单调栈的空间

C++

方法1: 枚举 每个元素作为山顶 O(n2)O(1)\lgroup O(n^2)、O(1) \rgroup

class Solution {
public:
    long long maximumSumOfHeights(vector<int>& maxHeights) {
        int n = maxHeights.size();
        long long res = 0;
        for (int i = 0; i < n; ++i){
            long long curSum = maxHeights[i];
            // 前缀 处理部分
            int pre = maxHeights[i];
            for (int j = i + 1; j < n; ++j){
                pre = min(pre, maxHeights[j]);
                curSum += pre;
            }
            // 后缀 处理部分
            pre = maxHeights[i];
            for (int j = i - 1;j >= 0; --j){
                pre = min(pre, maxHeights[j]);
                curSum += pre;
            }
            res = max(res, curSum);
        }
        return res;        
    }
};

⭐ 方法2: 前后缀分解 + 单调栈 O(n)\lgroup O(n) \rgroup 【空间换时间】

class Solution {
public:
    long long maximumSumOfHeights(vector<int>& maxHeights) {
        int n = maxHeights.size();
        long long res = 0;
        vector<long long> pre(n), suf(n);
        stack<int> st_pre, st_suf;
        
        // 处理 前缀 部分
        for (int i = 0; i < n; i++) {
            while (!st_pre.empty() && maxHeights[i] < maxHeights[st_pre.top()]) {
                st_pre.pop();
            }
             // 跳出 循环 有以下两种 情形, 分别讨论
            if (st_pre.empty()) {
                pre[i] = (long long)(i + 1) * maxHeights[i]; // 注意 个数 和 索引 之间的关系
            } else {
                pre[i] = pre[st_pre.top()] + (long long)(i - st_pre.top()) * maxHeights[i];
            }
            st_pre.emplace(i);
        }
        // 处理 后缀 部分
        for (int i = n - 1; i >= 0; i--) {
            while (!st_suf.empty() && maxHeights[i] < maxHeights[st_suf.top()]) {
                st_suf.pop();
            }
            // 跳出 循环 有以下两种 情形, 分别讨论
            if (st_suf.empty()) {
                suf[i] = (long long)(n - i) * maxHeights[i];
            } else {
                suf[i] = suf[st_suf.top()] + (long long)(st_suf.top() - i) * maxHeights[i];
            }
            st_suf.emplace(i);
            res = max(res, pre[i] + suf[i] - maxHeights[i]);
        }
        return res;
    }
};


// 参考链接:https://leetcode.cn/problems/beautiful-towers-ii/

2866. 美丽塔 II

题目链接

一样的题, 区别在于 这道题 的 O(n2)O(n^2) 解法 无法通过。

image.png

方法: 前后缀分解 + 单调栈 O(n)\lgroup O(n) \rgroup

Python3

class Solution:
    def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
        pre, suf, n = [0], [0], len(maxHeights)
        stack_pre, stack_suf = [-1], [n]  # 求 前后缀 时 分别维护一个单调递增 栈

        for i in range(n):
            while len(stack_pre) > 1 and maxHeights[stack_pre[-1]] > maxHeights[i]: # 大于 1 是 因为 加了  哨兵
                stack_pre.pop()   # 去掉 比 当前峰顶  大的
            pre.append(pre[stack_pre[-1] + 1] + maxHeights[i] * (i - stack_pre[-1]))  # 0 1 2 3   # 前面的前缀和 可以利用
            stack_pre.append(i)  ## 存储的是 下标

            j = n - 1- i 
            while len(stack_suf) > 1 and maxHeights[stack_suf[-1]] > maxHeights[j]:
                stack_suf.pop()

            suf.append(suf[n-stack_suf[-1]] + maxHeights[j] * (stack_suf[-1]-j))
            stack_suf.append(j)

        return max([ pre[i]+suf[n-i] for i in range(n) ])

C++

class Solution {
public:
    long long maximumSumOfHeights(vector<int>& maxHeights) {
        int n = maxHeights.size();
        long long res = 0;
        vector<long long> pre(n), suf(n);
        stack<int> st_pre, st_suf;
        
        // 处理 前缀 部分
        for (int i = 0; i < n; i++) {
            while (!st_pre.empty() && maxHeights[i] < maxHeights[st_pre.top()]) {
                st_pre.pop();
            }
             // 跳出 循环 有以下两种 情形, 分别讨论
            if (st_pre.empty()) {
                pre[i] = (long long)(i + 1) * maxHeights[i]; // 注意 个数 和 索引 之间的关系
            } else {
                pre[i] = pre[st_pre.top()] + (long long)(i - st_pre.top()) * maxHeights[i];
            }
            st_pre.emplace(i);
        }
        // 处理 后缀 部分
        for (int i = n - 1; i >= 0; i--) {
            while (!st_suf.empty() && maxHeights[i] < maxHeights[st_suf.top()]) {
                st_suf.pop();
            }
            // 跳出 循环 有以下两种 情形, 分别讨论
            if (st_suf.empty()) {
                suf[i] = (long long)(n - i) * maxHeights[i];
            } else {
                suf[i] = suf[st_suf.top()] + (long long)(st_suf.top() - i) * maxHeights[i];
            }
            st_suf.emplace(i);
            res = max(res, pre[i] + suf[i] - maxHeights[i]);
        }
        return res;
    }
};


// 参考链接:https://leetcode.cn/problems/beautiful-towers-ii/