攒青豆(动态规划解法)

86 阅读2分钟

当青训营遇上码上掘金

题目:攒青豆

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

725ef710a59944d49d0315bece7a1ac1_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png

从题目中可以看出我们最后计算的是管道组成的凹槽里面接住青豆的全部体积。

我们这里可以先计算出每条管道和青豆叠加在一起的最大高度,然后对每个最大叠加高度减去管道的高度得到青豆的高度,最后对其每条管道的青豆高度进行累加,然后就可以得到最后的结果

接下来就是解决如何计算每条管道和青豆叠加在一起的最大高度的问题

首先对于每条管道a[i]而言,它和青豆一起的最大高度height[i],取决a[i]为起点左边管道的最大高度leftMax,和以a[i]右边为起点管道的最大高度rightMax,两者的高度影响了a[i]存放青豆数量的上限,又因为木桶效应,上限的高度取决两个最大高度小的一方,也就是min(leftMax,rightMax),

我们可以通过动态规划的传递性求出最大值

定义动态规划数组 left[i],right[i]

left[i]表示以a[i]为起点往左的最大管道高度

right[i]表示以a[i]为起点往右边的最大管道高度 ** 这边得先对遍历的条件进行初始化,防止数组索引的溢出**


//边界条件判断,初始化left和right数组
left[0] =a[0];
right[a.length-1] = a[a.length-1];
for(int i=1,j=a.length-2;i<a.length;i++,j--){
    left[i] = Math.max(left[i-1],a[i]);
    right[j] = Math.max(right[j+1],a[j]);
}

求出后最后比较两者最小值减去管道高度值再累加即可

for(int i=0;i<a.length;i++){
    sum += Math.min(left[i],right[i])-a[i];
}
return sum;

下面就是完整的代码片段示例

public class ProQing {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a[]  =new int[]{5,0,2,1,4,0,1,0,3};
        int x =solve(a);
        System.out.println(x);
    }
    static int solve(int[] a) {
        //定义最大的青豆数
        int sum=0;
        //定义left[]数组
        //left[i]表示以a[i]为起点往左的最大管道高度
        int left[] = new int[a.length];
        //定义right[]数组
        //right[i]表示以a[i]为起点往右边的最大管道高度
        int right[] = new int[a.length];
        //边界条件判断,初始化left和right数组
        left[0] =a[0];
        right[a.length-1] = a[a.length-1];

        //动态规划分别通过i,j传递判断最大左边值和右边值
        for(int i=1,j=a.length-2;i<a.length;i++,j--){
            left[i] = Math.max(left[i-1],a[i]);
            right[j] = Math.max(right[j+1],a[j]);
        }
        //最后通过将(青豆和管道叠加高度)-(管道本身高度) 得到青豆高度累加求和;
        for(int i=0;i<a.length;i++){
            sum += Math.min(left[i],right[i])-a[i];
        }
        return sum;
    }

}