当青训营遇上码上掘金
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
直觉上能接住多少青豆,取决于某个位置两边最高柱子中小的那个,暴力解法,我们只需要对每个位置,计算其至最右边[rightMax]与最左边[leftMax]的最高柱子高度[min(leftMax,rightMax)],然后减去当前位置高度就得到这个位置能装多少豆子[min(leftMax,rightMax)]-height[i],显然这样子做的时间复杂度是o(n平方)
对上面的暴力方法进行优化,每个位置只需要知道左右最大柱子的高度,所以我们可以采用动态规划的思想,先求出每个位置左右柱子的最高高度。
1、创建两个数组leftMax与rightMax,数组下标i分别代表i位置左边或右边的最高柱子高度
2、递归式:
leftMax[i]=max(leftMax[i-1],height[i])
rightMax[i]=max(rightMax[i+1],height[i])
3.初始化: leftMax[0]=height[0]
leftMax[i-1]=height[i-1]
代码如下:
int[]leftMax=new int[n];
leftMax[0]=height[0];
for (int i=1;i<height.length;i++){
leftMax[i]=Math.max(leftMax[i-1],height[i]);
}
int[]rightMax=new int[n];
rightMax[n-1]=height[n-1];
for (int i=n-2;i>=0;i--){
rightMax[i]=Math.max(rightMax[i+1],height[i]);
}
此时,我们已经得到了rightMax、leftMax数组分别记录了某个位置i左右最高柱子,最后我们需要遍历每个位置i,并取左右最高柱子最小值减去当前位置柱子的高度,即可得到当前位置所能积攒的豆子数目,最后再做累加,即可输出答案
int ret=0;
for (int i=0;i<n;i++){
ret+=(Math.min(rightMax[i],leftMax[i])-height[i]);
}
System.out.println("答案是:"+ret);
完整代码如下:
public class Main {
public static void main(String[] args) {
int[]height=new int[]{5,0,2,1,4,0,1,0,3};
int n=height.length;
if (n==0)return ;
int[]leftMax=new int[n];
leftMax[0]=height[0];
for (int i=1;i<height.length;i++){
leftMax[i]=Math.max(leftMax[i-1],height[i]);
}
int[]rightMax=new int[n];
rightMax[n-1]=height[n-1];
for (int i=n-2;i>=0;i--){
rightMax[i]=Math.max(rightMax[i+1],height[i]);
}
int ret=0;
for (int i=0;i<n;i++){
ret+=(Math.min(rightMax[i],leftMax[i])-height[i]);
}
System.out.println("答案是:"+ret);
}
}
时间复杂度为o(3n)=o(n),本算法进行了3次遍历,空间复杂度为 o(2n)=o(n)