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

53 阅读1分钟

当青训营遇上码上掘金

攒青豆

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

攒青豆.png

样例

输入:height = [5,0,2,1,4,0,1,0,3]

输出:17

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

分析

类比力扣接雨水问题

解法一 暴力法

每格存豆子量=该格的左右两侧最大高度的较小者减去此格子的高度。只要能找到每个格子左右两侧的最大值就行。

需要注意求左侧和右侧最大值时,需要有j=i来比较当前值,如果不比较当前值height[j],可能会有左右两侧的值最大值都小于当前值,导致res为负。

class Solution {
    public int trap(int[] height) {
        int res = 0;
        for(int i=1;i< height.length-1;i++){
            int leftMax = 0,rightMax=0;
            //求左侧和右侧最大值时,需要有j=i来比较当前值,如果不比较当前值height[j],可能会有左右两侧的值最大值都小于当前值,导致res为负。
            for(int j=0;j<=i;j++){
                leftMax = Math.max(leftMax,height[j]);
            }
            for(int j=i;j<height.length;j++){
                rightMax = Math.max(rightMax,height[j]);
            }
            res+=Math.min(leftMax,rightMax) - height[i];
        }
        return res;
    }
}

解法二 动态规划

在上述的暴力法中,对于每个柱子,我们都需要从两头重新遍历一遍求出左右两侧的最大高度,这里是有很多重复计算的,很明显最大高度是可以记忆化的,具体在这里可以用数组边递推边存储,也就是常说的动态规划,DP。

定义二维dp数组 int[] [] dp = new int[n] [2],其中, dp[i] [0] 表示下标i的柱子左边的最大值, dp[i] [1] 表示下标i的柱子右边的最大值。 分别从两头遍历height数组,为 dp[i] [0]和 dp[i] [1] 赋值。

从前往后和从后往前是因为初始值。

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        int res = 0;
        int[][] dp = new int[n][2];
        dp[0][0] = height[0];
        dp[n-1][1]= height[n-1];
		//从前往后 
        for(int j=1;j<n;j++){
            dp[j][0] = Math.max(dp[j-1][0],height[j]);
        }
        //从后往前
        for(int j=n-2;j>=0;j--){
            dp[j][1] = Math.max(dp[j+1][1],height[j]);
        }
        for(int j=0;j<n;j++){
            res+=Math.min(dp[j][0],dp[j][1]) - height[j];
        }
        return res;
    }
}

之后还有单调栈,双指针法再补充。