攒青豆解题分项 | 青训营

38 阅读2分钟

**当青训营遇上码上掘金 **

作为一个热爱编程的学生,经常会参加各种编程竞赛来锻炼自己的技能。 而这次青训营给我们带来了一道关于攒青豆的问题。

题目大意是:有n个宽度为1的柱子,根据n个非负整数表示每个柱子的高度,排列后从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。

从题目中我们可以看出,我们需要求出有多少个青豆能在柱子间形成的矩形中,这显然是一道求面积的题目。

有两种解法:暴力法和双指针法。

暴力解法

暴力法的思路是枚举每个柱子,把它当作矩形的左右边界,并找到与其高度最短的柱子作为上下边界,以此计算面积。但是这样的算法复杂度为O(n^2), 无法在规定时间内通过此题。

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        int res = 0;
        for (int i = 0; i < n; i++) {
            int leftMax = 0, rightMax = 0;
            for (int j = i; j >= 0; j--) {
                leftMax = max(leftMax, height[j]);
            }
            for (int j = i; j < n; j++) {
                rightMax = max(rightMax, height[j]);
            }
            res += min(leftMax, rightMax) - height[i];
        }
        return res;
    }
};

这里我们用两个循环分别找到了每个柱子左右两边的最高柱子,然后用左右最高柱子的较小值减去当前柱子的高度来计算当前柱子围成的矩形的面积。最后,将所有柱子的面积累加,得到最终答案。

双指针

而双指针法的思路是:从左到右枚举每个柱子作为左边界,寻找右边的第一个比左边高的柱子作为右边界,再比较左右两边柱子的高度,选取最矮的作为面积的高度,并用这个度计算出面积,同时维护两个指针来更新右边界,以找到更高的高度。这样的算法复杂度为O(n), 可以在规定时间内通过此题。

#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> heights(n);
    for (int i = 0; i < n; i++) {
        cin >> heights[i];
    }

    int left = 0, right = n - 1;
    int maxArea = 0;
    while (left < right) {
        // 计算当前矩形的面积
        int curArea = (right - left) * min(heights[left], heights[right]);
        // 更新最大面积
        maxArea = max(maxArea, curArea);
        // 比较左右柱子的高度,选择较矮的那一边移动
        if (heights[left] < heights[right]) {
            left++;
        } else {
            right--;
        }
    }

    cout << maxArea << endl;
    return 0;
}

我们可以看到代码实现中,我们使用了左右两个指针来更新计算面积。同时,我们也使用了较为简洁的代码,来描述这一过程,使得代码的可读性和可扩展性大大提高。

总的来说,双指针法比暴力法的优势在于时间复杂度的降低,使得代码能够在规定时间内通过此题,同时也使得代码的可读性和可扩展性有了很大的提高。