-
当青训营遇上码上掘金
-
主题 4:攒青豆
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
分析:两个柱子能存储豆子是这两个柱子之间的柱子的高度都比这两个柱子低,因此可以考虑使用单调栈,使用单调递减的栈。
单调栈是栈中元素始终具有单调性,如果要插入的元素会破坏栈中元素所代表的柱子高度的单调性,则使栈中元素退栈来保持栈的单调性。
当向单调栈插入一个值时,考虑比较当前栈的顶部值与要插入的值,如果当前栈的值比要插入的值小,说明当前栈的下一个柱子与要插入的柱子之间可以存储豆子,存储的豆子数量为(当前栈的柱子的高度-两个柱子之间的柱子高度)乘以两个柱子之间的距离,然后将当前栈顶的值退栈,继续比较,直到栈空或当前栈的值比要插入的值大或等于,然后将要插入的值插入;如果当前栈的值比要插入的值大于或等于,则直接插入值。
在每一次插入值并且栈中元素要退栈时,我们都要计算当前柱子与前面的柱子所能存储的豆子数,退栈过程中使用上面的公式计算豆子数,退栈结束后,如果栈中还有元素,则还要计算一次上述公式,因为当前栈顶元素代表的柱子比要插入的柱子高,并且两个柱子之间还有柱子,能够存储豆子。
计算公式理解:
代码:
`
#include<iostream>
#include<stack>
#include<vector>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, b,sum=0;
cin >> n;
stack<int> a;
vector<int> res; //存储数据
for (int i = 0; i < n; i++)
{
cin >> b;
res.push_back(b);
}
for (int i = 0; i < n; i++)
{
int tt = 0; //存储前一个柱子的高度
while (!a.empty() && res[a.top()] < res[i]) //保持栈的单调,递减
{
//两个柱子之间豆子容量 = 两柱子高度差 * 两柱子距离
sum += (res[a.top()] - tt) * (i - a.top() - 1);
tt = res[a.top()];
a.pop();
}
if (!a.empty())
{
sum += (res[i] - tt) * (i - a.top() - 1);
}
a.push(i);
}
cout << sum << endl;
}
`