当青训营遇上码上掘金
题目
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积),不得不说非常贴切活动主题hh,大家一起来卷青豆吧!
解题思路
只有类似于“凹”型区域才能接住青豆,如果柱子的高度一直是单调递增或单调递减的,则无法接住青豆,而一旦先递减再递增时,则形成凹形区域,能够接住青豆,故可以使用单调栈来模拟该问题。创建一个单调递减栈,当柱子的高度是单调递减时,入栈;当出现某个柱子比栈顶柱子高时,说明此时可以攒一些青豆了。
到现在为止,确定了更新青豆数量的时机,需要进一步处理的,就是如何计算的问题了。当单调递减栈有两个及两个以上元素时,若遇到比栈顶柱子高的柱子时,栈顶第二个柱子m、栈顶柱子n和新遇到的柱子k构成一个“凹”型,凹形区域能够攒的青豆数则为 cnt = (k - m - 1) * [min(heightm, heightk) - heightn],该凹形区域填满青豆后,从上空往下看,柱子n不会再影响后续攒豆子的行为,故出栈即可。
单调递减栈遇到一个比栈顶柱子高的柱子时,更新一次青豆数量,此时栈顶柱子出栈,为了保持单调递减栈的性质,继续比较现存的栈顶柱子,迭代进行,直到比栈顶柱子矮或者栈为空,执行入栈操作。
现在还有一个问题就是,似乎程序执行完,栈中可能还有元素,这是否合理呢?答案是合理的,因为从高空往下看,现存的柱子是单调递减的,而这又是无法构成凹形区域来攒豆子的。
代码
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
int trap(vector<int>& height)
{
int n = height.size(), result = 0;
stack<int> s;
for(int i = 0; i < n; i++)
{
while(!s.empty() && height[s.top()] < height[i])
{
int left = s.top();
s.pop();
if(s.empty()) break;
int h = min(height[i], height[s.top()]) - height[left];
result += h * (i - s.top() - 1);
}
s.push(i);
}
return result;
}
int main()
{
vector<int> height = {5, 0, 2, 1, 4, 0, 1, 0, 3};
cout << trap(height) << endl;
return 0;
}