「青训营 X 码上掘金」主题创作活动-主题4
当青训营遇上码上掘金
主题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 个单位的青豆。
思路
这道题目其实就是“接雨水”的翻版,只是把雨水换成了青豆。本题有两种解法:动态规划和单调栈。本文我将介绍动态规划法。
动态规划采用按列计算的方法,如果按照列来计算的话,宽度一定是1了,再把每一列的青豆的高度求出来就可以了。
当前列青豆值:min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度。
把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight)。这样就避免了重复计算,这就用到了动态规划(每一个状态一定由上一个状态推导出来)。
leftMax[i] = max(height[i], leftMax[i - 1]);
rightMax[i] = max(height[i], rightMax[i + 1]);
最后将每一列青豆值为正的值进行累加,得到最终能接到的青豆总数。
具体代码实现如下:(注意在读取输入时是无限循环,利用cin.get()函数读取回车结束输入)
#include <iostream>
#include <vector>
using namespace std;
int getPeas(vector<int>& height)
{
int size = height.size();
int res = 0;
vector<int> leftMax(size, 0);
leftMax[0] = height[0];
vector<int> rightMax(size, 0);
rightMax[size - 1] = height[size - 1];
// 记录左边柱子的最高高度
for (int i = 1; i < size; ++i)
{
leftMax[i] = max(leftMax[i - 1], height[i]);
}
// 记录右边柱子的最高高度
for (int i = size - 2; i >= 0; --i)
{
rightMax[i] = max(rightMax[i + 1], height[i]);
}
// 计算青豆总数
for (int i = 0; i < size; ++i)
{
int s = min(leftMax[i], rightMax[i]) - height[i]; // 每一列的青豆值
if (s > 0) res += s;
}
return res;
}
int main()
{
vector<int> height;
int num = 0;
// 注意在读取输入时是无限循环,利用cin.get()函数读取回车结束输入
while (1)
{
cin >> num;
height.push_back(num);
if (cin.get() == '\n')break;
}
int res = getPeas(height);
cout << res << endl;
system("pause");
return 0;
}