当青训营遇上码上掘金
题目描述:现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
题意示意图:
图中黑色部分为柱子,绿色部分为青豆,示例中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
*/