当青训营遇上码上掘金
题目
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
思路
看完题目后,一眼接雨水
这道题目可用单调栈解决:
单调栈:
单调栈是一种理解起来很容易,但是运用起来并不那么简单的数据结构。一句话解释单调栈,就是一个栈,里面的元素的大小按照他们所在栈内的位置,满足一定的单调性。
思路
栈头元素:凹槽底部的柱子,栈头的第二个元素:凹槽左边的柱子,添加的元素:凹槽右边的柱子
从栈头到栈底应该是从小到大的顺序,要添加的元素(右边的柱子)发现大于栈顶元素(凹槽底部)了
我们分三种情况:
-
当遍历到的元素小于栈顶元素即height[i] < height[st.top()] 时,入栈
if(height[i] < height[st.top()]) { st.push(i);} -
当遍历到的元素等于栈顶元素时,将栈顶元素出栈并入栈顶元素
if (height[i] == height[st.top()]) { st.pop(); st.push(i);} -
当遍历到的元素大于栈顶元素时,出栈顶元素直到栈顶元素大于等于该元素,然后进行计算:高取左边界和右边界的最小值减去凹槽底部,底取i - st.top() - 1
while(!st.empty() && height[st.top()] < height[i]) { int mid = st.top(); st.pop(); if(!st.empty()) { int h = min(height[st.top()], height[i]) - height[mid]; int r = i - st.top() - 1; sum += h * r; } }st.push(i); // 最后别忘记该元素还要做凹槽的左边
遍历完成后,sum即为最后答案
举例:(为了方便,上面的数字表示索引对应的高度)
- 当遍历到元素2时(2 > 0):
- 当遍历到元素4时 (4 > 1):
当计算完上面两小块后,栈内还剩元素5、2(同样为了方便表示),再继续计算4左边的剩余青豆数,计算完成后,左半边计算完毕
代码(C++)
#include<iostream>#include<vector>#include <stack>using namespace std;int trap(vector<int> &height) { if(height.size() <= 2) { return 0; } stack<int> st; st.push(0); int sum = 0; for(int i = 1; i < height.size(); i++) { if(height[i] < height[st.top()]) { st.push(i); }else if (height[i] == height[st.top()]) { st.pop(); st.push(i); }else { while(!st.empty() && height[st.top()] < height[i]) { int mid = st.top(); st.pop(); if(!st.empty()) { int h = min(height[st.top()], height[i]) - height[mid]; int r = i - st.top() - 1; sum += h * r; } } st.push(i); } } return sum;}int main() { vector<int> height; int x; while (cin >> x) { height.push_back(x); if (cin.get() == '\n') break; } cout << endl; cout << trap(height) << endl; return 0;}