当青训营遇上码上掘金——攒青豆

81 阅读2分钟

当青训营遇上码上掘金————攒青豆

题解

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

解题思路:每一列能接住多少青豆,取决于当前列,还有左边最高的柱子和右边最高的柱子。找到左边最高的柱子和右边最高的柱子二者中的最小值,如果当前列的值大于等于这个最小值,则当前列无法保存青豆,否则当前列的值减去这个最小值就是当前列所能保存的青豆数,总的青豆数等于各列之和

那么遍历一下所有列,然后每一次再遍历出左边最高的柱子和右边最高的柱子,并求最小值,根据当前列和最小值的关系即可得出当前列能攒的青豆,全部加起来就可以得出总的青豆数

而每一次都遍历左边最高的柱子和右边最高的柱子太浪费了,可以使用动态规划优化一下,使用leftMax,rightMax来保存左边最高的柱子和右边最高的柱子,一次遍历就可以得出所有列的左边最高的柱子和右边最高的柱子

左边最高的柱子求法为拿上一列的左边最高的柱子和上一列的高度求最大值,右边最高的柱子求法为拿下一列的右边最高的柱子和下一列的高度求最大值

代码

public static int saveGreenBeans(int[] heights) {
        int sum = 0;
        int[] leftMax = new int[heights.length];
        int[] rightMax = new int[heights.length];
​
        for (int i = 1; i < heights.length; i++)
            leftMax[i] = Math.max(leftMax[i - 1], heights[i - 1]);
        for (int i = heights.length - 2; i >= 0; i--)
            rightMax[i] = Math.max(rightMax[i + 1], heights[i + 1]);
        for (int i = 1; i < heights.length - 1; i++) {
            int min = Math.min(leftMax[i], rightMax[i]);
            if (min > heights[i])
                sum += min - heights[i];
        }
        return sum;
}