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

33 阅读2分钟

题目 攒青豆

笑死了 不知道哪个人出的这题目 很应景

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

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

以下为上图例子的解析: 

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

emmmm这其实就是接雨水问题,用单调栈或者DP或者双指针都能解决。

可以把每一组青豆按列划分,每一列能够攒的数量是可以一直增加的,就比如说从[5,0,2]到[5,0,2,1,4],在第二列能攒的青豆从2颗变成了4颗。 也就是说最大数量是与两侧各自最高的柱子中较低的一个保持平齐。

如果用指针来说的话就是借助两个指针分别从两端出发,每次去较低的高度,计算结果,然后直到遇到高度相等或者更高的柱子,知道两个指针相遇得到最后结果。

解法一:暴力

public static int solution(int[] height){
    int res = 0;
    for (int i = 1; i < height.length - 1; i++) {
        int leftMax = 0, rightMax = 0;
        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;
}
  • 首先就是要便利每个柱子,这里采用for循环依次遍历
  • 然后计算当前柱子和左侧柱子的最大高度,记录最大值,小的那个就是能够攒青豆的最大数量,用Math.min()
  • 然后再计算右侧柱子中的最大值
  • 对结果进行累加,用当前柱子高度减去左右两个最大高度的较小值即可

解法二:双指针

什么是双指针?

在左指针扫描时,如果遇到大于基准值的元素,则继续扫描,如果遇到小于等于基准值的元素,则停止扫描。在右指针扫描时,如果遇到小于等于基准值的元素,则继续扫描,如果遇到大于基准值的元素,则停止扫描。然后我们交换左指针和右指针所指的元素。这样我们就将数组分成了两个部分。接下来,我们递归地(不一定)对左部分和右部分进行排序,直到整个数组有序。

qingdou := [5,0,2,1,4,0,1,0,3]
n := len(qingdou)
left, right := 0, n-1
leftMax, rightMax := 0, 0
res := 0
for left < right {
if qingdou[left] < qingdou[right] {
    if qingdou[left] >= leftMax {
	leftMax = qingdou[left]
    } else {
	res += leftMax - qingdou[left]
    }
    left++
} else {
    if qingdou[right] >= rightMax {
	rightMax = qingdou[right]
    } else {
	res += rightMax - qingdou[right]
    }
    right--
}

在循环中比较两个指针指向的高度,若左指针高度低,则说明其左侧可以攒住青豆,将left_max更新 在 while 循环中,我们比较两个指针所指向的位置的高度,如果左指针所指向的高度小于右指针所指向的高度,则说明左指针所指向的位置可能存在青豆,我们将 left_max 更新为当前左右指针之间的最大值,并计算能存的数量,否则计算右边的。最后在指针相遇时结束。