当青训营遇上码上掘金
题目
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
分析
我们分别考虑这n个宽度为1的柱子,每个柱子上面能放多少青豆。显然,由于问题发生在二维平面上,限制一个“坑”能接多少青豆的,是其左右边界中较矮的那个。这就是所谓的“木桶效应”,一个木桶能装多少水,取决于其最短的那一块木板。比如题图中,2 3 4号柱子被5号柱子限制,其总高度最多为4;6 7 8号柱子被9号柱子限制,其总高度最多为3。
然后,我们分别考虑左、右边界的限制。以右边界为例,我们再次转换思路,考虑一个柱子作为右边界,能够限制它左边的多少柱子?既然这样,我们从每根柱子的顶部向左画线,直到碰到别的比它高的柱子挡住线,或是到底了为止。显然每根柱子对应的线就是其限制范围,因为要是碰到了比它高的柱子,那么两个边界中较矮的那个,一定就是它自己了(那根比它高的柱子作为左边界)。
上图是对题图的分析。然后对于每一根柱子,如果它头顶上有多根红线,那我们肯定贪心地取最高的那一条,作为它能存放的最大青豆高度。
如何找出最高的红线?我们可以发现,由于红线向左画到比它高的柱子就停了,因此一根红线是最高的那一条,当且仅当它对应的柱子右边,所有的柱子都比它矮。题图中,5 9号柱子对应的红线满足要求。
对于左边界的分析亦然。
在每根柱子顶上所有蓝线里面,我们也取最高的那一条。同理,一根蓝线是最高的那一条,当且仅当它左边没有比它更高的柱子。题图中只有1号柱子满足。
每根柱子顶上取出的红蓝线取最小值,就是这根柱子能放的最大高度。题图中,高度均被红线限制。
实现
经过缜密的分析,实现并不难了。以左边界为例,我们记录一个最大值,初始为0。然后从左向右扫描,遇到比最大值更大的就更新最大值。之后记录当前柱子的最大高度为此最大值。
preMax := 0 //初始化最大值
for i := 0; i < n; i++ {
if height[i] > preMax {
preMax = height[i] //更新最大值
}
maxh[i] = preMax //记录柱子最大高度
}
右边界亦然,只不过此时还要跟刚才记录的左边界去最小值。
preMax = 0
for i := n - 1; i >= 0; i-- {
if height[i] > preMax {
preMax = height[i]
}
if maxh[i] > preMax {
maxh[i] = preMax //跟左边界取最小值
}
}
最后每根柱子的最大高度减去它自身高度,全部相加即为答案。