当青训营遇上码上掘金之“攒青豆”

66 阅读1分钟

当青训营遇上码上掘金

题目

主题 4:攒青豆

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

image.png

以下为上图例子的解析:

输入:height = [5,0,2,1,4,0,1,0,3]  
输出:17  
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

思路

使用单调栈,单调栈常见模型:找出每个数左边离它最近的比它大/小的数。

int tt = 0;
for(int i = 1; i <= n; i++) {
    while(tt && check(stk[tt],i)) tt--;
    stk[++tt] = i;
} 
  1. 单调栈分为单调递增栈和单调递减栈
    • 单调递增栈即栈内元素保持单调递增的栈
    • 同理单调递减栈即栈内元素保持单调递减的栈
  2. 这里我们选择单调递减栈
    • 当后面的柱子高度低于前面的,无法兜住青豆。
    • 当找到一根比前面高的柱子,开始计算攒的青豆。
  3. 对于位置靠后,但是柱子比前面低的柱子,直接入栈
  4. 当出现比栈顶更高的柱子时,
    • 收集前面柱子攒的青豆
      • 取栈顶元素,保存cur,出栈。
      • 取底 为当前柱子与当前栈顶柱子的距离
      • 高为cur 和当前柱子与当前栈顶柱子最小值 的差值
      • 计算收集的青豆,底 * 高
    • 将栈顶比当前的柱子更低的柱子出栈
  5. 循环,直到最后一根柱子
#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
int res;
int a[N];
int stk[N] ,tt;

int main() {
    for(int i = 1; (scanf("%d", &a[i]))!=EOF; i++) {
        // scanf("%d", &a[i]);
        while(tt && a[i] > a[stk[tt]]) {
            int cur = stk[tt--];
            if(!tt) break;
            int s = i - stk[tt] - 1;
            int h = min(a[stk[tt]], a[i]) - a[cur];
            res += s * h;
        }
        stk[++tt] = i;
    }
    printf("%d", res);
    return 0;
}