[单调栈]:122.最大乘积问题

97 阅读2分钟

问题描述

小R最近遇到了一个数组问题。他有一个包含 N 个元素的数组,记作 a1,a2,...,aN。为了分析这个数组的特性,小R定义了两个函数 L(i) 和 R(i),并希望通过这两个函数来找到一些有趣的结论。

  • L(i) 是满足以下条件的最大的 j 值:
  • j<i
  • a[j]>a[i]
  • 如果找不到这样的 j,那么L(i)=0;如果有多个满足条件的 j,选择离 i 最近的那个。
  • R(i) 是满足以下条件的最小的 k 值:
  • k>i
  • a[k]>a[i]
  • 如果找不到这样的 k,那么R(i)=0;如果有多个满足条件的 k,选择离 i 最近的那个。

最终,小R定义 MAX(i)=L(i)∗R(i),他想知道在1≤iN 的范围内,MAX(i) 的最大值是多少。


测试样例

样例1:

输入:n = 5, array = [5, 4, 3, 4, 5] 输出:8

样例2:

输入:n = 6, array = [2, 1, 4, 3, 6, 5] 输出:15

样例3:

输入:n = 7, array = [1, 2, 3, 4, 5, 6, 7] 输出:0

前置知识

单调栈参考题解:
juejin.cn/post/743456…

解题思路

  • 简化一下题意:对于aia_i,找到左右两边第一个大于aia_i的数的下标(下标从1开始),两个下标的乘积记作tit_i,求最大的tit_i

  • 朴素做法,遍历aa数组,内嵌往左和往右的循环

    • 简单实现
    • for(int i=0; i < a.size(); i++) {
          int l,r;
          for(l = i -1; l >= 0 && a[l] <= a[i]; l--);
          for(l = i + 1; r < a.size() && a[r] <= a[i]; r++);
          if(r == a.size())r = 0;
          ans = max(ans, (l + 1) * r);
      }
      
  • 考虑优化做法

  • 先考虑左边的情况,我们如果优化找到左边第一个大于aia_i的数的下标

    • 很容易想到使用单调栈来解决,不断出栈直到a[st.top]>aia[st.top] > a_i,此时ti=st.top()+1t_i = st.top()+1,此时栈中存放的下标,+1是因为下标从1开始
  • 考虑右边的情况,两种做法

    • 仿照左边的做法,从右往左遍历数组即可
    • 将当前aia_i作为右端点,那么栈中<=ai<=a_i的元素的右端点就是aia_i
  • 举个例子

    • a=[5,3,4,5]a = [5,3,4,5],设左栈leftleft,右栈rightright,数组cntcnt记录乘积

    • a0=5a_0=5

    • 左栈无值,加入下标0, left=[0]left = [0];右栈无值,加入下标, right=[0]right=[0]

    • a1=3a_1 = 3

      • 左栈出栈直到栈顶大于aia_i, 此时cnt[1]=0+1=1cnt[1] = 0+1 = 1(左边下标+1)
      • 此时设a1a_1为右端点,右栈中均大于等于a1a_1
    • 左栈加入,left=[0,1]left=[0,1];右栈加入right=[0,1]right=[0,1]

    • a2=4a_2 = 4

      • 左栈..., cnt[2]=0+1cnt[2] = 0 + 1
      • a2a_2为右端点, 此时栈顶a[r.top()]=3<a2a[r.top()] = 3 < a_2, 那么cnt[r.top()]=i+1cnt[r.top()] *= i+1,此时i=2i = 2, 可以看出,此时cnt[1]cnt[1]已经记录了左右端点的下标乘积
    • ...

核心代码

int solution(int n, std::vector<int> array) {
    int ans = 0;
    stack<int>l, r ;
    vector<int>cnt(n);
    for(int i = 0; i < n; i++) {
        while(!l.empty() && array[l.top()] <= array[i])
            l.pop();
            
        if(!l.empty())
            cnt[i] = l.top() + 1;
        
        while(!r.empty() && array[i] > array[r.top()]) {
            cnt[r.top()] *= i+1;
            ans = max(ans, cnt[r.top()]);
            r.pop();
        }
        l.push(i);
        r.push(i);

    }
    return ans;
}