「青训营 X 码上掘金」主题创作活动

85 阅读1分钟

「青训营 X 码上掘金」主题创作活动

当青训营遇上码上掘金

问题

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

image.png
以下为上图例子的解析: 

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

问题思路

经典的接雨水问题,我这里使用前后缀分解的方式来解决。

对于上面的每一个柱子,都可以看作是两边有挡板隔着:

image.png

这样的话,左边可能接住的豆子数量为从左往右的柱子最大值,右边可能接住的豆子数量为从右往左的柱子最大值,而该柱子能接住的豆子个数为前缀最大和后缀最大与柱子高度之差的最小值。

image.png

代码

时间复杂度:O(n)O(n)

空间复杂度:O(n)O(n)

import java.util.Scanner;

/**
 * @author : fly_beep
 */
public class Main {
    public static void main(String[] args) {
        System.out.print("Please input the height : ");
        Scanner sc = new Scanner(System.in);
        String str = sc.next();
        // 输入以 , 分隔
        String[] strs = str.split(",");
        long result = bean(strs);
        System.out.println("The result is : " + result);
    }

    public static long bean(String[] data) {
        // 使用前后缀分解算法
        int n = data.length;
        long[] preMax = new long[n],sufMax = new long[n];
        // 前缀最大
        preMax[0] = Long.parseLong(data[0]);
        for (int i = 1; i < n; i++) {
            preMax[i] = Math.max(preMax[i-1], Long.parseLong(data[i]));
        }
        // 后缀最大
        sufMax[n-1] = Long.parseLong(data[n-1]);
        for (int i = n-2; i >= 0 ; i--) {
            sufMax[i] = Math.max(sufMax[i+1], Long.parseLong(data[i]));
        }

        long ans = 0;
        for (int i = 0; i < n; i++) {
            ans += Math.min(preMax[i], sufMax[i] - Long.parseLong(data[i]));
        }
        return ans;
    }
}

码上掘金代码地址