攒青豆 | 「青训营 X 码上掘金」主题创作
当青训营遇上码上掘金
题目介绍
攒青豆
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
题目分析
这题需要用到简单的动态规划。先对数组中的所有元素进行一次预处理:从右往左遍历找到每一根柱子右侧最高的柱子,再从左往右遍历找到每一根柱子左侧最高的柱子。对于每一柱子,能攒青豆的量就是左右两侧最高柱子的最小值与当前柱子的高度的差值,最后,将所有能接住的青豆数量相加。
这题原题就在leetcode上,只是换了个背景,我很早之前还提交过42. 接雨水。由于对码上掘金并不熟悉也不会使用(他调试还要自己写在html的吗?根本不知道写哪,不过我代码是可以跑的),我在c++窗口写上了类ACM模式的代码并且合并了我leetcode的代码,没有做更多的创作。
思路模拟
求每一列能攒的青豆数,我们只需要关注当前列,左边最高的墙,右边最高的墙就够了。
青豆的数量,根据短板效应,我们只需要看当前柱子左边最高的柱子和右边最高的柱子中较矮的一个的高度。
所以,根据较矮的那个柱子和当前列的柱子的高度可以分为三种情况。
-
较矮的墙的高度大于当前列的墙的高度
很明显,较矮的一边,也就是左边的柱子的高度,减去当前列的高度就可以了。
-
较矮的墙的高度小于当前列的墙的高度
正在求的列不会有青豆,因为它大于了两边较矮的墙。
-
较矮的墙的高度等于当前列的墙的高度。
和上一种情况是一样的,不会有青豆留下。
明白了这三种情况,程序就很好写了,遍历每一列,然后分别求出这一列两边最高的墙maxl和maxr。找出较矮的一端,和当前列的高度比较,结果就是上边的三种情况。
这样遍历每一列左边最高的柱子和右边最高的柱子的时间复杂度是O(n^2)。
但是
我们注意到对于每一列,我们求它左边最高的柱子和右边最高的柱子(不包括自己),都是重新遍历一遍所有高度,这里可以优化。
首先利用两个数组,l[i]表示第i列柱子左边最高的柱子的高度,r[i]代表第i列柱子右边最高的柱子的高度。
对于l[i]可以遍历height,l[i] = max(l[i - 1], height[i - 1])
得到,同理r[i]可由r[i] = max(r[i + 1], height[i])
得到,注意边界条件。这样,对于直接模拟无需每次重新遍历一次求maxl和maxr了。
最后对于每一列res += max(0, min(l[i], r[i]) - height[i - 1])
,当前列表示左右两边最搞的柱子中更低的一个,减去当前列柱子的高度即是当前列可以攒的青豆数。
时间复杂度:O(n)。空间复杂度:O(n)。
显然更优。
代码
#include<bits/stdc++.h>
using namespace std;
class Solution {
public:
int l[20002], r[20002];
int trap(vector<int>& height) {
l[0] = 0;
r[height.size()] = 0;
for (int i = 0; i < (int)height.size(); i ++) l[i + 1] = max(l[i], height[i]);
for (int i = height.size() - 1; i >= 0; i --) r[i] = max(r[i + 1], height[i]);
int res = 0;
for (int i = 1; i <= (int)height.size(); i ++) res += max(0, min(l[i], r[i]) - height[i - 1]);
return res;
}
};
void solve() { //为什么没有调试,不会用,手写个调试的东西
int n;
vector<int> v;
Solution sol;
cin >> n; //柱子个数
for(int i = 1, x;i <= n;i ++) {
cin >> x;
v.push_back(x); //输入柱子高度
}
cout << sol.trap(v) << endl;
return;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
solve();
return 0;
}
/*
sample input:
9
5 0 2 1 4 0 1 0 3
sample output:
17
*/
后言
青训营的活动规则实在是太多,想要结营,在无法获得四等奖及以上的情况下必须所有的活动都全部全勤,而运营并没有很清楚的说明规则,或者是说明的太乱,导致很多人或多或少的没做完,比如我的阅读打卡我今天才发现还缺了一个限制,还无法修改我的阅读打卡内容。目前的18天里面有没有1天都是个问题。我知道这些规则很有利于掘金社区的内容创作,但是并没有讲清楚缺了xx就会无法计数什么的,也没有挽回的办法。希望运营能写的更清楚些吧,最好搞整合的文档而不是东一个西一个,少点限制和要求,总是害怕又漏了什么规则没看,导致多天的学习创作攒不了青豆,这些规则并没有利于技术的学习,还会影响学员的结营证书和参营积极性,属实是失望......