当青训营遇上码上掘金之主题四:攒青豆

53 阅读2分钟

当青训营遇上码上掘金

这道题自己也没有想出啥自己学过的算法来解决,但是还是有点想法的,原先刷蓝桥杯题目的时候好像也遇到过类似的题目。好像叫消竹子,感觉差不多,只是问法不一样那么就开始陈述我的解题步骤了。

题目

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

图片.png

思考过程

这道题问的是可以最多可以装多少豆子,一开始还想向背包问题上靠,但是发现这个如果是背包问题的话,那这个背包太不规律了吧?所以打消了这个念头,然后我就想起原先刷的题,其实可以反向思维一下,这道题其实有点像木桶装水,但是不是一个桶,而是多个,为啥这样说呢,看图5,和4中间可以装多少就是以4为最大值,4和3中间,3又是最高值,但是下面那些小的木板又会占零空间,不如这样想,在两个高的模板中间,除去短木板站的空间就是青豆占的位置,那我们就可以拆木板 5,0,2,1,4,0,1,0,3

图片.png 我们抽去最底的一层,那么这一层可以装3个绿豆,如图。 然后我们再抽去现在的最底一层。这一层可以装5个,

图片.png

后面以此类推

图片.png

图片.png

也就是3+5+6+3=17,我觉得是时候动代码了

最终解决方法

将上面的思路转化为代码的话就是一个数组然后遍历它n(n为数组中最大的数)次,然后每次都减去一 每次循环减完一后再遍历数组,如果数组现在里面有<=0的话就判断它的前面和后面有没有正数,如果有,就豆子数加一。 但是这样的话实现代码就得3重for循环了,这也太麻烦了吧,所以我就采用单调栈的写法

    int qindo(vector<int>& height) {
        stack<int> sta;
        sta.push(0);
        int sum = 0;
        for (int i = 1; i < height.size(); i++) {
            while (!sta.empty() && height[i] > height[sta.top()]) {
                int mid = sta.top();
                sta.pop();
                if (!sta.empty()) {
                    int h = min(height[sta.top()], height[i]) - height[mid];
                    int w = i - sta.top() - 1;
                    sum += h * w;
                }
            }
            sta.push(i);
        }
        return sum;
    }