「青训营 X 码上掘金」接青豆

115 阅读3分钟

当青训营遇上码上掘金

题目

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

图片.png

分析

我们分别考虑这n个宽度为1的柱子,每个柱子上面能放多少青豆。显然,由于问题发生在二维平面上,限制一个“坑”能接多少青豆的,是其左右边界中较矮的那个。这就是所谓的“木桶效应”,一个木桶能装多少水,取决于其最短的那一块木板。比如题图中,2 3 4号柱子被5号柱子限制,其总高度最多为4;6 7 8号柱子被9号柱子限制,其总高度最多为3。

然后,我们分别考虑左、右边界的限制。以右边界为例,我们再次转换思路,考虑一个柱子作为右边界,能够限制它左边的多少柱子?既然这样,我们从每根柱子的顶部向左画线,直到碰到别的比它高的柱子挡住线,或是到底了为止。显然每根柱子对应的线就是其限制范围,因为要是碰到了比它高的柱子,那么两个边界中较矮的那个,一定就是它自己了(那根比它高的柱子作为左边界)。

图片.png

上图是对题图的分析。然后对于每一根柱子,如果它头顶上有多根红线,那我们肯定贪心地取最高的那一条,作为它能存放的最大青豆高度。

如何找出最高的红线?我们可以发现,由于红线向左画到比它高的柱子就停了,因此一根红线是最高的那一条,当且仅当它对应的柱子右边,所有的柱子都比它矮。题图中,5 9号柱子对应的红线满足要求。

对于左边界的分析亦然。

图片.png

在每根柱子顶上所有蓝线里面,我们也取最高的那一条。同理,一根蓝线是最高的那一条,当且仅当它左边没有比它更高的柱子。题图中只有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 //跟左边界取最小值
	}
}

最后每根柱子的最大高度减去它自身高度,全部相加即为答案。