青训营遇上码上掘金之攒青豆

59 阅读2分钟

当青训营遇上码上掘金

主题4:攒青豆

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

image.png

我们竖着考虑每一列的青豆数量。很容易发现下面几个规律

  • 当一个柱子的左边和右边有比他高的柱子的时候,该列能攒下来的青豆数是 该柱左侧最高的柱子和该柱右侧最高的柱子的小值 - 该柱子的高度
  • 当一个柱子的左边或者右边没有比他高的柱子时,该列不能存下青豆

所以,我们只需要记录下每一列的左侧和右侧有没有比他高的柱子,比他高的柱子最高又是多高,就可以用min(lmax,rmax) - h算出该列能攒的青豆数量

于是我们维护每个下标从 最左边 到 该下标柱子 和从 最右边 到 该下标 最高的柱子是哪个就可以了。 如何维护呢? 从左到右我们比较lmax[i - 1]height[i] 的大小,较大的那个就是柱子的高度了。

代码如下


    int trap(vector<int>& height) {
        int n = height.size();
        vector<int>lmax(n),rmax(n);
        for(int i=0;i<n;i++){
            lmax[i] = max((i - 1 < 0 ? 0 : lmax[i - 1]),height[i]);
        }
        for(int i=n-1;i>=0;i--){
            rmax[i] = max(((i + 1 > n - 1) ? 0 : rmax[i + 1]),height[i]);
        }
        int res = 0;
        for(int i = 1; i < n - 1; i ++){
            int tmp = min(rmax[i + 1],lmax[i - 1]);
            if(tmp > height[i]) res += tmp - height[i];
        }
        return res;
    }

时间复杂度O(N) 只需要常数次遍历数组,空间复杂度O(N) 我们需要和 height 同一数量级的空间来存放辅助数组 lmax 和 rmax的值。

当然,该题也有其他解法,由于题意可以转换为求该下标之后最高的柱子,符合单调栈的简单应用,可以构造一个递减的栈,每次当一个比当前栈内末尾元素大的元素将要进栈时,计算该空出的区域能够接的青豆数量。