当青训营遇上码上掘金
本次活动,选择的题目为主题4:攒青豆。不同于题目三的一眼背包问题,动态规划。这道题目比较容易想出有意思的其他解法。且相比单调栈,动态规划,意义更明确,清晰易懂
面对算法题,一般分四步走
- 问题分析:抽象场景问题为数学问题
- 算法:寻找适合的算法思路
- 分析复杂度:分析解法的时间复杂度,空间复杂度
- 编程实现
问题分析
原题:
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
抽象
分析该题目,接住青豆多少,取决于凹槽容积多大。
形成的凹槽容积,是短边×底-槽底,题目就可转换为,如何便捷的获取不同的凹槽的边。或者说,如何确定当前索引能达到的青豆高度。如何求 目标数组(填满青豆)与当前数组值差。
算法
结合原题目的图,我们不难发现,对于最大值在数组边上的,从另一头开始的单调不减序列,能分级的判断容量。同样的,最大值在数组中间的,就从两边的单调序列,注意边界值即可。
所以很容易得出一种获得短边的方法如下:
- 从数组起始,求其单调不减序列,存储其位置
- 对该序列,除该序列最后一个值外,当前的最大值都决定了之后的豆子高度
- 将能达到的高度与该位置值相减,累加得到序列末尾的青豆
- 若数组末尾值小于序列末尾值(即最大值位于中间:不可能大于,等于即位于末尾),即可以单调递增的方式直接遍历。
复杂度
最多遍历2n,时间复杂度为O(n)。需要构建单调队列,空间复杂度为O(n)
编程实现
使用java的队列,来构造单调队列。代码如下