当青训营遇上码上掘金
题目描述
主题 4:攒青豆现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
例:
输入:height = [5,0,2,1,4,0,1,0,3] 输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
动态规划法
首先我们可以用一个式子表示我们最终要求得的解,即某一位置左右两边柱子高度的较小值,减去此位置本身的高度,即是解的表达,表达式可写成:
空间复杂度:O(n),其中 n 是数组 height 的长度。需要创建两个长度为 n 的数组 leftMax 和 rightMax
result = Math.min(max_left[i],max_right[i]) - height[i]
接下来我们要通过遍历每个位置的情况对result进行累加,首先必须记录每个位置的高度情况,放入两个数组中,一个代表位置左边的高度情况,一个代表位置右边的高度情况,首先第一个和最后一个位置显然是不能放青豆的,所以在开始时直接置0,之后循环遍历max_left数组与max_right数组,并为两数组中每一位置情况赋值,表达式如下:
max_left[i] = Math.max(max_left[i-1],height[i-1]);
max_right[i] = Math.max(max_right[i+1],height[i+1]);
完整代码如下:
import java.util.*;
// 动态规划法
public class Main {
public static void main(String []args) {
// Scanner inputHeight = new Scanner(System.in);
int[] height = {5,0,2,1,4,0,1,0,3};
System.out.println(count(height));
}
public static int count(int[] height) {
int n = height.length;
int[] max_left = new int[n];
int[] max_right = new int[n];
int ans = 0;
// 第一个和最后一个位置不能放青豆
max_left[0] = 0;
max_right[n-1] = 0;
if(n < 2) return 0;
for(int i = 1;i < height.length;i++){
// 一个位置左边所能提供的最大高度
max_left[i] = Math.max(max_left[i-1],height[i-1]);
}
for(int i = n-2;i >= 0 ;i--){
// 一个位置右边所能提供的最大高度
max_right[i] = Math.max(max_right[i+1],height[i+1]);
}
for(int i = 1;i < n-1;i++){
// 判断避免为负
if(Math.min(max_left[i],max_right[i]) > height[i]){
// 累加
ans += Math.min(max_left[i],max_right[i]) - height[i];
}
}
// 返回青豆数
return ans;
}
}
复杂度分析
时间复杂度:O(n),其中 n 是数组 height 的长度。计算数组 max_left 和 max_right 的元素值各需要遍历数组 height 一次,计算青豆总量还需要遍历一次。空间复杂度:O(n),其中 n 是数组 height 的长度。需要创建两个长度为 n 的数组 leftMax 和 rightMax