「青训营 X 码上掘金」入营版 主题创作4——攒青豆

73 阅读2分钟

当青训营遇上码上掘金

主题创作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 个单位的青豆。
image.png

思路:
可以使用动态规划
进行预处理,创建长度为nn的两个数组llrr,分别记录下标i(0<i<n)i(0<i<n)位置和左边位置的Height的最大高度,rr则表示ii和右边位置的Height的最大高度。l[0]l[0]hs[0]hs[0]r[n1]r[n-1]hs[n1]hs[n-1]
各个位置的l[i]l[i]r[i]r[i]的取值如下:

{l[i]=hs[i],i=0 r[i]=hs[i],i=n1 \begin{cases} l[i] = hs[i], & i=0 \ \\ r[i]=hs[i], & i=n-1 \ \end{cases}
{l[i]=max(hs[i],l[i1]),1in1 r[i]=max(hs[i],r[i+1]),0in2 \begin{cases} l[i] = max(hs[i], l[i-1]), & 1\le i \le n-1 \ \\ r[i] = max(hs[i], r[i+1]),& 0\le i \le n-2 \ \end{cases}

之后分别进行正向遍历和反向遍历Height数组,ii处能接到的最大青豆数量为min{l[i],r[i]hs[i]}min\lbrace l[i],r[i]-hs[i]\rbrace,对每个位置进行遍历求和得到总量,算法的复杂度为O(n)O(n)。

代码:

环境:码上掘金
语言:Go

package main

import "fmt"

func getAns(hs []int) (ans int) {
    //获取数组长度赋值为n
    n := len(hs)
    //判断n是否为0,是则直接返回
    if n == 0{
      return
    }
    //l为记录左半部分最大值的数组,长度为n
    l := make([]int, n)
    l[0] = hs[0]

    //l[i]为数组求最值
    for i := 1; i < n; i++ {
      l[i] = max(hs[i],l[i-1])
    }

    //r为记录右半部分最大值的数组,长度为n
    r := make([]int, n)
    r[n-1] = hs[n-1]

    for i := n - 2; i >= 0; i-- {
      r[i] = max(hs[i],r[i+1])
    }
    //遍历相加
    for i, hs := range hs{
      ans += min(l[i], r[i]) - hs
    }

    return
}

//求最小值
func min(a, b int) int {
    if a < b{
      return a
    }
    return b
}
//求最大值
func max(a, b int) int {
    if a > b{
      return a
    }
    return b
}

func main() {
    /*
        var hs = []int{5,0,2,1,4,0,1,0,3}
        ans := getAns(hs)
        fmt.Println("按此排列的柱子能接住的青豆数量为:", ans)
    */
    var num int
    fmt.Scan(&num)
    hs := make([]int, num)
    for i := 0; i < num; i++ {
        fmt.Scan(&hs[i])
    }
    ans := getAns(hs)
    fmt.Println("按此排列的柱子能接住的青豆数量为:", ans)
}