当青训营遇上码上掘金之攒青豆
有幸参加第五届字节跳动青训营,和大伙一起进步,一起学习,下面是我对主题4:攒青豆的思考与解法,欢迎大家参考学习~
一、题目大意
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
二、题目分析
这道题有点像木桶效应,但又不完全一样,要注意青豆如何不会漏出去,需要三根柱子才能攒住青豆,为此要计算攒青豆的数量,至少有三根柱子,像两根柱子是不行的。
如上图,两个柱子不能攒青豆,只有三根柱子才能攒青豆,这里将三根柱子的高度分别记为lhs,mid,rhs,能攒的青豆数目记为nums,则求解nums的伪代码:
nums=0;
height = min(lhs,rhs)-mid;
width = rhs-lhs-1;
nums += height*width;
这里是求解的关键部分,下面便是如何取得lhs、mid、rhs:
我们知道单调的柱子高度是不会攒到青豆,只有乱序的柱子才会攒青豆,为此需要一个容器存储这些柱子的高度,而且还可以处理单调的问题,于是想到了单调栈。
单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是只需要遍历一次。
在使用单调栈的时候首先要明确如下几点:
-
单调栈里存放的元素是什么?
- 单调栈里只需要存放元素的下标i就可以了,如果需要使用对应的元素,直接T[i]就可以获取。
- 单调栈里元素是递增呢? 还是递减呢?
注意一下顺序为从栈顶到栈底的顺序。
这里我们要使用递增循序(再强调一下是指从栈头到栈底的顺序),因为只有递增的时候,加入一个元素i,才知道栈顶元素在数组中右面第一个比栈顶元素大的元素是i。
使用单调栈主要有三个判断条件。
- 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
- 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
- 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
三、题目解答
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
int handler(vector<int>& height) {
stack<int> stk;
stk.push(0);
int len = height.size();
int res=0;
for(int i=1;i<len;i++){
while(!stk.empty()&&height[i]>height[stk.top()]){
int mid_idx = stk.top();
int mid = height[mid_idx];
stk.pop();
if(!stk.empty()){
int lhs = stk.top();
int h = min(height[lhs],height[i])-mid;
int w = i-lhs-1;
res+=h*w;
}
}
stk.push(i);
}
return res;
}
int main() {
vector<int> height;
int num;
while(cin>>num){
height.push_back(num);
if(cin.get() == '\n')
break;
}
int res = handler(height);
cout<<res<<endl;
return 0;
}
四、总结与思考
这道题的单调栈和以往不同,不存储实际的值,而是存储下标,便于取得所需要的宽度,这使得计算更加方便。