当青训营遇上码上掘金

806 阅读3分钟

当青训营遇上码上掘金

主题四 __ 攒青豆 __ C++实现

话不多说,先看题目描述

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

来自青训营官方账号

例子解析:

输入:height = [5,0,2,1,4,0,1,0,3]
输出:17

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


先上源码(阅读源码先思考)

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

int main(){
    vector<int> arrheight{5,0,2,1,4,0,1,0,3}; //示例数据或者使用cin>>
     int n = arrheight.size();
    if(n < 2){
        return 0;
    }
    int l = 0; 
    int r = n - 1;
    int mark = min(arrheight[l], arrheight[r]);// 找出左右边界的最小值作为柱子的高度
    long sum = 0;
    while(l < r){
        if(arrheight[l] < arrheight[r]){ // 如果左边较低,则左边界向右遍历, 否则右边界向左移动
            l++; 
            if(arrheight[l] < mark){// 如果当前柱子小于mark,则青豆量累加
                sum += mark - arrheight[l];
            }else{// 否则,将当前柱子和右边边界高度进行比较,找出剩下数组中的新的最低的柱子
                mark = min(arrheight[r], arrheight[l]);
            }
        }else{
            r--; // 和上面同理,如果右边较低,则右边界向左遍历
            if(arrheight[r] < mark){// 同理,如果当前柱子小于mark,则青豆量累加
                sum += mark - arrheight[r];
            }else{// 否则,将此标尺和左边界的高度进行比较,找出剩余数组中的新水位
                mark = min(arrheight[l],arrheight[r]);
            }
        }
    }
    cout<<sum<<endl;
    return 0;
}

解析(可以先阅读源码进行思考)

本题是一个较为经典的hard难度的题目,同时也有多种解法,本文的解法为:指针法,可以将将空间复杂度降到 O(1)。 也有其他解法, 例如:每层青豆数总和,单调栈等等,各位看官可以多多了解


  1. 容器能接青豆的多少取决于较低的那一方,所以我们的指针总是从较低的那一方开始移动。如果初始化右边更低,则先移动r指针,反之亦然。指针 l 只会向右移动,指针 r 只会向左移动,然后初始化两个指针:l = 0;r = arrheight.size() - 1;
  2. 并且一直维护一个mark变量记录两个当前指针最小柱子的高度。
  3. 如果l小于r,则表示mark变量是l,则l可以向右移动,并与mark进行比较,只要当前的 l 指向的柱子小于mark,则证明当前的l指向的柱子与mark指向的柱子必定可以接住青豆,并且根据1.的第一句话,容器能接青豆的多少取决于较低的那一方,就得出此次的青豆数为 mark - arrheight[l];然后l向右移动。r同理。然后利用sum进行累加。
  4. 如果3.中,l比make大,则表明当前r和mark指向的柱子之间并不能接住青豆,所以需要重新计算mark的值,把剩下的r和l的最小值重新赋给mark,继续3.4.的步骤。

个人解题,所以可能错误,可能算法还有优化的地方,仅供参考。