青训营 X 码上掘金 | 主题 4:攒青豆

45 阅读2分钟

当青训营遇上码上掘金

1.题目描述:

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

2c2628dbe68b4ed390fd99c3e8cbba14_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.awebp 以下为上图例子的解析:

输入:height = [5,0,2,1,4,0,1,0,3]  
输出:17  
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

2.简单分析:

首先我想到的是暴力求解,因为在这个题目背景下暴力求解的时间复杂度并不高;

然后我想到了动态规划,对应这道题应该采用“双指针”来记录每个位置其左右最高的柱子,然后取两者中小的与当前位置高度作差,便是当前位置可以容纳的青豆的高度,再逐一累加即可。

当然,在其他相关文章中我还学习到了如单调栈等我不熟知的方法。

3.详细过程:

定义两个数组LCurrentMax和RCurrentMax,分别代表左右的柱子最大高度(实际上可以不用数组,两个指针变量即可)。其中,LCurrentMax从前向后遍历,RCurrentMax从后往前遍历。

这样会在前面循环赋值的过程中实时更新当前位置可以填充的约束范围,即动态规划记录过程。最后在每组L和R中取小的那一个,这样就省去了比较LCurrentMax和RCurrentMax的语句。

4.源代码:

def main() -> None:
    height = [5, 0, 2, 1, 4, 0, 1, 0, 3]
    sumFill=0
    LCurrentMax= [0] * len(height)
    RCurrentMax= [0] * len(height)

#每个位置它的左边最高墙
    tempH=height[0]
    for i in range(0, len(height)):
        tempH=max(height[i],tempH)
        LCurrentMax[i]=tempH

#每个位置它的右边最高墙
    tempH=height[len(height)-1]
    for i in range(len(height)-1,-1,-1):
        tempH=max(height[i],tempH)
        RCurrentMax[i]=tempH

#每个位置可以装青豆的高度
    for i in range(0, len(height)):
        packedHeight=min(LCurrentMax[i],RCurrentMax[i])
        sumFill+=packedHeight-height[i]

    print(sumFill)

if __name__ == '__main__':
    main()

5.运行结果:

Snipaste_2023-02-13_16-47-23.png

6.总结:

经典算法要吃透并融会贯通,要坚持刷刷算法题,在一道道具体题目中得到思维提升,所谓“Show me your code”,手搓代码和看看学学是完全不一样的,有空还是得多练习才不会生疏。