当青训营遇上码上掘金
攒青豆 —— 题目内容
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆,及简化的木桶效应
思路与算法
通过枚举每个柱子的高度,寻找在这根柱子之前出现的最高柱子或者不比这根柱子矮的柱子①,同时在寻找这根柱子时记录比当前柱子矮的柱子高度和,在构成“木桶”后需要减去这个值,比如上图中的[4,0,1,0,3]这个“木桶”,构成的大小为 3 * 3 = 9,但是需要减去 0 + 1 + 0 = 1
①:这根柱子之前出现的最高柱子或者不比这根柱子矮的柱子
最高柱子:因为如果是最高的柱子,那么在这根柱子之前,已经没有柱子可以和当前柱子构成更高的“木桶”了
不比这根柱子矮的柱子:和上面的同理,这根柱子已经比当前柱子高了,前面更高的柱子也只能和这根较高的柱子构成“木桶”了
例如:[5,1,4,2,3], 会构成两个“木桶”,[5,1,4] 与 [4,2,3]
代码
删除了输入输出部分,具体查看码上掘金的源代码的注释
func catchGreenBeans(pillars []int64) int64 {
pillarNumber := len(pillars)
beans := make([]int64, pillarNumber)
calculate(&pillars, &beans, 0, pillarNumber, 0)
return beans[pillarNumber-1]
}
// 计算
func calculate(pillars *[]int64, beans *[]int64, index int, number int, max int64) {
// 索引超出切片直接返回
if index >= number {
return
}
// 当前柱子的高度
cur := (*pillars)[index]
// 因为是一根柱子直接设置为当前柱子中的最大高度即可
if index == 0 {
max = cur
} else {
// 因为柱子占位需要减去的豆子数
var cut int64 = 0
// 木桶效应中的矮个子
var height int64 = 0
i := index - 1
for ; i >= 0; i-- {
pre := (*pillars)[i]
if cur > pre {
// 说明从当前柱子比所有之前的所有柱子都高,直接结束循环
if pre == max {
max = cur
height = pre
break
}
cut += pre
} else { // 当出现不比当前柱子矮的柱子时也能直接结束循环
height = cur
break
}
}
// 到当前柱子能接住的豆子数的计算方法就是
// 前面最高的柱子或者不比当前柱子矮的柱子的豆子数
// 加上
// 这根柱子到当前柱子通过通过木桶校园能装的豆子数
// 减去
// 两根柱子之间被矮个柱子挤出来的豆子
(*beans)[index] = (*beans)[i] + int64(index - i - 1) * height - cut
}
// 递归计算
calculate(pillars, beans, index+1, number, max)
}
复杂度分析
时间复杂度:O(n^2),其中 n 是柱子中的元素数量。最坏情况下任意柱子都需要要和之前的柱子比较一次
空间复杂度:O(n)。这里使用的是递归的方式,需要占用额外空间,可以改成循环