攒青豆

1,041 阅读2分钟

当青训营遇上码上掘金

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

题意示意图: image.png 图中黑色部分为柱子,绿色部分为青豆,示例中height数组为[5,0,2,1,4,0,1,0,3],青豆容量为17=4+2+3+3+2+3;

解题思路: 对于下标 i,青豆能到达的最大高度等于下标 i 两边的最大高度的最小值,下标 i 处能接的青豆量等于下标 i 处的水能到达的最大高度减去 height[i]。

如果使用暴力的方法,对于每一个位置height[i]都需要向两边进行一次遍历,复杂度为O(n),总共有n个位置,所以总的复杂度为n的平方。如果事先知道每个位置两边的最大高度,则可以在 O(n) 的时间内得到能接的雨水总量,因此考虑用动态规划的方法来降低复杂度。

动态规划做法如下: 使用两个数组leftmax[]和rightmax[]来记录位置 i 两侧的最大高度,显然leftmax[0]=height[0],rightmax[n-1]=height[n-1].有了这个初始状态,分别向右向左遍历,即可获得leftmax[]和rightmax[]的值。

计算过程如下:leftmax[i]=max(leftmax[i-1],height[i]);rightmax[i]=max(rightmax[i+1],height[i]);

最后通过一次循环,计算出总的青豆数量。int ans = 0; for(int i =0;i<n;i++) ans+=min(leftmax[i], rightmax[i]) - height[i];

使用动态规划以后,算法的复杂度降至O(n),同时为了节省内存占用,数组采用vector容器代替。

需要注意的是,事先并不知道height[]数组的长度,所以输入时应采用while语句进行控制,具体实现策略见code部分。

code:

#include<iostream>
#include<vector>
#include<cmath>

using namespace std;

void printvector(vector<int> v)
{
	for(int i=0;i<v.size();i++) cout<<v[i]<<endl;
}

//解题思路:动态规划
 
int main() {
  vector<int> height;
  
  //输入height数组 
  int a;
  while(cin>>a)
  {
  	height.push_back(a);
  	if(cin.get()=='\n') break;
  }
   
  //计算leftmax和rightmax 
  int n = height.size();
  vector<int> leftmax(n);
  vector<int> rightmax(n);
  leftmax[0]=height[0];
  rightmax[n-1]=height[n-1];
  for(int i=1;i<n;i++)leftmax[i]=max(leftmax[i-1],height[i]);
  for(int i=n-2;i>=0;i--) rightmax[i]=max(rightmax[i+1],height[i]);
  
  //计算青豆总量
  int ans = 0;
  for (int i = 0; i < n; ++i) {
      ans += min(leftmax[i], rightmax[i]) - height[i];
  }
  cout<<ans<<endl;
//  printvector(leftmax);
  return 0;
}

/*
in:0,1,0,2,1,0,1,3,2,1,2,1
out:6

in:4,2,0,3,2,5
out:9

in:5,0,2,1,4,0,1,0,3
out:17
*/