攒青豆 | 「青训营 X 码上掘金」主题创作

99 阅读4分钟

当青训营遇上码上掘金

很开心能够加入到第五届青训营来拓宽自己的视野,学习新的知识。我将通过本文分享我参加本次主题创作的过程和心得,不完善和可优化的地方还请各位多多指教,大家一起快乐敲码~

主题介绍

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

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

主题分析

通过对主题的理解可以分析出,能接多少青豆主要是看柱子构成的边界是哪一部分。

首先,为了构成一个能够接青豆的容器需要有左边界右边界,因此我们就需要找到最左边的柱子位置和最右边的柱子位置;

其次,处于左右边界内的柱子可能会将一个大容器分为多个小容器,因此我们需要按照一定的顺序找到每个小容器的边界

最后,对于每个小容器来说,计算青豆数的方法都是一样的,已确定边界的位置就可以知道容器的宽度,已知边界的高度就以较小高度的边界为容器的高度且每个柱子宽度都为1,因此小容器的青豆数通过容器内每列**(较矮边界高度-柱子高度)的值进行累加**得到。

主题实现

  • 实现语言:C++
  • 柱子高度:引入<vector>用于构建存储柱子高度的容器,与数组相比其优点在于不需要预先确定长度,主题中柱子的数目也是未知可变换的。
  • 左/右边界:顺序循环遍历柱子高度,找到第一个高度不为0的柱子并记录为左边界的位置;倒序循环遍历柱子高度,找到第一个高度不为0且在左边界右侧的柱子记录为右边界的位置,若没有符合要求的左/右边界,即表明未形成容器,可以接0个单位的青豆。
  • 小容器边界及高度:我采用从左到右寻找小容器的方式,所以小容器的左边界都是确定的,要寻找的就是右边界。若有柱子比左边界高,则小容器确定且高度为左边界高度;若找不到比左边界高的柱子,则寻找剩余柱子中最高的作为小容器的右边界且小容器高度为右边界高度。直到小容器的右边界为大容器右边界时寻找结束。
  • 青豆数计算:因为每一个柱子宽度都为1,所以每列可用高度代替单位个数,每个小容器的青豆数累加可得大容器青豆数,小容器内每列的青豆数累加可到小容器青豆数,每列青豆数可由小容器高度减去该列柱子高度得到。

具体代码实现如下:

#include <iostream>#include <vector> //引入向量,一种顺序容器using namespace std;int main(){  vector<int> height;  int number, len, i, left_i=-1, right_i=-1, border_i=-1, low_border;  int s = 0, border = 0;  cout << "请依次输入柱子高度:" << endl;  while (true)  {    cin >> number;    height.push_back(number);    if (cin.get() == '\n')      break;  }  len = height.size();  //左边界  for ( i = 0; i < len; i++)  {    if (height[i]!=0)    {      left_i = i;      break;    }  }  if (left_i==-1)  {    cout << "可以接0个单位的青豆" << endl;    exit(0);  }  //右边界  for ( i = len-1; i > left_i; i--)  {    if (height[i] != 0)    {      right_i = i;      break;    }  }  if (right_i == -1)  {    cout << "可以接0个单位的青豆"<<endl;    exit(0);  }  //找到最近最高边界  while (left_i!=right_i)  {    for (i = left_i+1; i <= right_i; i++)    {      if (height[i] > height[left_i])      {        border_i = i;        low_border = height[left_i];        break;      }      else      {        if (height[i] > border)        {          border = height[i];          border_i = i;          low_border = border;        }      }    }    //累加面积    for (i = left_i + 1; i < border_i; i++)    {      s = s + low_border - height[i];    }    left_i = border_i;    border = 0;  }  cout << "可以接" << s << "个单位的青豆" << endl;  return 0;}

码上掘金-攒青豆

心得总结

我认为解决该主题问题的重点在于对边界的确认,通过大容器边界确保大容器存在,通过小容器边界将问题分为多个相同的小问题进行解决。这是一种比较容易想到的方法,但所用到的循环较多可能影响性能,不完善和可优化的地方还请各位多多指教。