「青训营 X 码上掘金」一起来收集青豆

78 阅读2分钟

当青训营遇上码上掘金

字节跳动第五届青训营已经过半啦,大家的青豆都攒的怎么样啦,反正我还在努力攒青豆,也算是在学习的过程中对自己的记录和鼓励。不过,现在有一个难题摆在我们面前,情况是这样的:

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

在这种条件下,我们怎么做才能拿到最多的青豆数量呢?有什么明确的方法吗?这里我来贡献一个自己的方法。考虑到本次青训营课程中学习并使用Go语言,这里也尝试用Go语言解决这一问题。

先分析题目:什么时候青豆才会被收集起来呢?我们单独看一列柱子会发现,当该柱子的左右两边都存在高于该柱子的柱子时,就会有青豆因为凹陷被收集。那收集的数量又怎么统一呢?可以发现每一个柱子收集的青豆数量取决于本身的柱子高度和左右两边最高柱子中小的那一个,已知柱子的宽度为1,那么可以这样计算每一个柱子处的青豆数量:(左右最高柱子中矮的那个高度-本身的高度)* 宽度1。按照这种思路,从左往右计算一遍,便可得到接住的青豆总数了。

我这里采用动态规划的思想解决问题,主要思想就是采用两个数组left和right,保存每一个位置的左右最高的柱子高度,这样仅需要一次遍历就可以计算出的青豆总数,时间复杂度为O(n)。具体代码如下:


//判断并返回两数大者
func max(a,b int)int{ 
    if a>b{
        return a
    }
    return b
}

//判断并返回两数小者
func min(a,b int)int{
    if a<b{
        return a
    }
    return b
}

//计算青豆数
func caculBeans(height []int) int {
    res:=0
    n:=len(height)
    left:=make([]int,n)
    right:=make([]int,n)
    left[0]=height[0]
    right[n-1]=height[n-1]
    //left计算并保存左侧最高的柱子
    for i:=1;i<n;i++{
        left[i]=max(left[i-1],height[i])
    }
    //right计算并保存左侧最高的柱子
    for i:=n-2;i>=0;i--{
        right[i]=max(right[i+1],height[i])
    }
    //利用保存的left、right计算青豆总数
    for i:=1;i<n-1;i++{
        tmp:=min(right[i],left[i])-height[i]
        if tmp>0{
            res += tmp
        }
    }
    return res
}

这样便可以解决收集青豆的问题。