当青训营遇上码上掘金--主题 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 个单位的青豆。
贪心算法思路
看到这道题目的时候,我选择了一个贪心的算法
先找到最高的两个柱子,先算最高的两个柱子之间能得到多少豆子,若这两个柱子是最边上的,那就直接打印总数并结束;若不在边上,那就找下一个高的柱子和刚刚的第二高的柱子所得到的豆子
也就是说,我会先将所有柱子按高到低排列并存入结构体的数组里,结构体的x值为数组索引,y值为高度。然后从高到低循环计算每两个柱子之间的豆子。
为什么贪心
若高柱子离得越远,循环次数就越少
若再优化一下,增加一点条件判断语句,甚至可以不用再计算已经计算过的豆子
如何结束循环
采用计数方法,新创建一个对应索引值的数组key,每当遍历到一个高度y值后,其索引在key中的值更改为-1,直到最后key中所有值为-1即可结束循环
待优化:可以用切片,每当遍历到一个高度y值后,在key中删除这个值,直到key为空时结束循环
解决重复计算豆子的问题
按照这种贪心算法,会存在重复计算豆子的问题,如:[3,0,5,0,4,1],会先计算5-4之间的豆子,然后计算4-3之间的豆子,可是5-4是被4-3所包含的,所以会存在多次计算同一地方的豆子
解决方案:给予计算豆子一个条件,例如4-3,若4-3中间出现比3大的柱子就跳过。但是5-4之间的0还是会被重复计算呢,那该怎么办呢,那就在计算完5-4之间的豆子之后以及计算4-3之前,把已经计算过的豆子改为5-4之间较小的值,即为4。同理计算完4-3后,改变其中间的柱子高度为3。这样就完美解决问题了。
代码
package main
import (
"fmt"
"sort"
)
func main() {
m := make(map[int]int)
height := []int{5, 0, 2, 1, 4, 0, 1, 0, 3}
key := make([]int, len(height))
for i := 0; i < len(height); i++ {
m[i] = height[i] //将数组的索引和值对应为map
key[i] = i //key是索引值,x值
}
type position struct {
x int
y int //x对应索引,y对应高度
}
var lstposition []position
for x, y := range m {
lstposition = append(lstposition, position{x, y})
}
sort.Slice(lstposition, func(i, j int) bool {
return lstposition[i].y > lstposition[j].y // 降序
})
//以上为结构体数组按柱子高度降序的排序
sum := 0
//以下为计数以及计算总值的代码
for i := 0; ; i++ {
if lstposition[i].x < lstposition[i+1].x {
for j := lstposition[i].x; j <= lstposition[i+1].x; j++ {
if j == lstposition[i].x || j == lstposition[i+1].x {
} else {
if lstposition[i+1].y > height[j] {
sum += lstposition[i+1].y - height[j]
height[j] = lstposition[i+1].y
}
}
key[j] = -1
}
} else {
for j := lstposition[i+1].x; j <= lstposition[i].x; j++ {
if j == lstposition[i].x || j == lstposition[i+1].x {
} else {
if lstposition[i+1].y > height[j] {
sum += lstposition[i+1].y - height[j]
height[j] = lstposition[i+1].y
}
}
key[j] = -1
}
//以下为结束循环的条件
count := 0
for j := 0; j < len(key); j++ {
if key[j] == -1 {
count++
}
}
if count == len(key) {
//fmt.Println("count:", count)
fmt.Println(sum)
break
}
}
}
}