当青训营遇上码上掘金
题目描述: 现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
问题分析:
- 跟接雨水问题很像,我们只关心每个位置自己上方可以留下多少青豆,仅受当前位置两边的高度影响。
举列:当前 i 位置是 5, 左边最大值 10, 右边最大值 20
-
那么当前位置肯定会停 5 个水,因为超过 10 的话肯定都会从左边溜走了,20再大也无法存放青豆。
-
所以我们需要
Math.min(数组0到i-1位置最大值,数组i+1到结尾位置最大值)-nums[i]这个就是对于当前数来说是至少能装的青豆,短的位置决定上限,因此取左右位置最大值的最小值,再减去当前位置的高度就是可以存放的青豆数量。- 如果这个结果负数,那么当前数就是 0, 能存 0 个青豆,说明该位置是最高位置,无法存放青豆,必须是一个“凹”的位置才可以存放青豆。
所以最终是
Math.max(Math.min(数组0到i-1位置最大值,数组i+1到结尾位置最大值)-nums[i],0) -
所以只要给每一个位置的都算出这个,让他们加一起就是答案,第一个位置和最后一个除外,因为他们无法存放青豆。
-
时间复杂度 O (n)
你可能会想用两个辅助数组分别存每一个位置他左边到他的最大值,以及他到他右边的最大值
代码如下:
/**
* 支持 import Java 标准库 (JDK 1.8)
*/
import java.util.*;
/**
* 注意:目前 Java 代码的入口类名称必须为 Main(大小写敏感)
*/
public class Main {
public static void main(String[] args) {
int[] arr = {5,0,2,1,4,0,1,0,3};
int res1 = process(arr);
System.out.println(res1);
}
public static int process(int[] arr){
int[] leftMax = new int[arr.length];
int[] rightMax = new int[arr.length];
leftMax[0] = 0;
leftMax[1] = arr[0];
rightMax[arr.length-1] = 0;
// 左边最大
for (int i = 2; i < arr.length-1; i++) {
int x = arr[0];
for (int j = 1 ; j < i;j++){
x = Math.max(x,arr[j]);
}
leftMax[i] = x;
}
// 右边最大
for(int i = 1 ; i < arr.length-1;i++){
int m = arr[2];
for (int j = i+1;j< arr.length;j++){
m = Math.max(m,arr[j]);
}
rightMax[i] = m;
}
int res = 0;
// 除首尾位置,计算每一个位置的青豆数
for(int i = 1 ; i < arr.length-1;i++){
int tmp = Math.max(Math.min(leftMax[i],rightMax[i])-arr[i],0);
res += tmp;
}
return res;
}
}