当青训营遇上码上掘金 - 攒青豆(C++版)

74 阅读2分钟

问题回顾

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

解决思路

刚上手这个问题时,如果我们第一个想法可能是简单的只从一侧向另一侧循环遍历进行计算:

下一列的的青豆数量 = 这列或之前列最高的高度 - 下一列的高度

但是如果最高的柱子位于中间位置,那么在最高柱子之后位置的计算都会出现错误导致偏大

所以我们不妨先将最高的柱子的个数位置找出来,此时整个图像则会被最高的柱子分为两个(或多个)区域

而每个区域此时相当于确定了一侧的高度,能装青豆的数量也完全由另一侧和底面长度决定

此时,我们再次遍历划分的每个区域,记录遇到的最高的柱子,则:

每一列的青豆数量 = 遇到的最高列高度 - 当前列高度

由于是从最高柱子的另一侧开始遍历,在计算最后一个区块时将方向倒转即可

代码部分

在代码部分中,我使用了数组来存放每一个最高的柱子的位置

因为是同一个方向的遍历,数组中数字位置的前后也是最高柱子出现的先后顺序,方便了后续划分区域的计算

#include <iostream>
using namespace std;


int main() {
    int arr[] = {5, 0, 2, 1, 4, 0, 1, 0, 3};
    int len = sizeof(arr) / sizeof(arr[0]);
    int maxl = 0, num = 0;
    int *pos = new int[len];
    for (int i = 0; i < len; i++){
        if (arr[i] >= maxl){
            maxl = arr[i];
        }
    }
    for (int i = 0; i < len; i++){
        if (arr[i] == maxl){
            pos[num] = i;
            num++;
        }
    }
    int ans = 0, ptr = 0;
    for (int i = 0; i < num; i++){
        int nowmax = 0;
        for (; ptr < pos[i]; ptr++){
            if (arr[ptr] > nowmax){
                nowmax = arr[ptr];
            }
            else{
                ans += nowmax - arr[ptr];
            }
        }
        ptr++;
    }
    int nowmax = 0;
    for (ptr = len - 1; ptr > pos[num - 1]; ptr--){
        if (arr[ptr] > nowmax){
            nowmax = arr[ptr];
        }
        else{
            ans += nowmax - arr[ptr];
        }
    }
    cout << ans;
}