攒青豆

41 阅读2分钟

攒青豆

当青训营遇上码上掘金。在青训营 X 码上掘金创作活动中,我选择主题4攒青豆来进行创作。这道题其实和LeetCode43一模一样。解题的思路和方法也多种多样。在这里我挑两种最经典的方法来说一下。

方法一 动态规划

创建两个长度为 nn 的数组 leftMaxleftMaxrightMaxrightMax。对于 0i<n0 \leq i<nleftMax[i]leftMax[i] 表示下标 ii 及其左边的位置中,heightheight 的最大高度,rightMax[i]rightMax[i] 表示下标 ii 及其右边的位置中,heightheight 的最大高度。

显然,leftMax[0]=height[0]leftMax[0]=height[0]rightMax[n1]=height[n1]rightMax[n−1]=height[n−1]。两个数组的其余元素的计算如下:

  • 1in11 \leq i \leq n−1 时,leftMax[i]=max(leftMax[i1],height[i]) leftMax[i]=max(leftMax[i−1],height[i]);
  • 0in20 \leq i \leq n−2 时,rightMax[i]=max(rightMax[i+1],height[i])rightMax[i]=max(rightMax[i+1],height[i])

因此可以正向遍历数组 heightheight 得到数组 leftMaxleftMax 的每个元素值,反向遍历数组 heightheight 得到数组 rightMaxrightMax 的每个元素值。

在得到数组 leftMaxleftMaxrightMaxrightMax 的每个元素值之后,对于 0i<n0 \leq i < n,下标 ii 处能接的雨水量等于 min(leftMax[i],rightMax[i])height[i]min(leftMax[i],rightMax[i])−height[i]。遍历每个下标位置即可得到能接的雨水总量。

方法二 单调栈

除了计算并存储每个位置两边的最大高度以外,也可以用单调栈计算能接的雨水总量。维护一个单调栈,单调栈存储的是下标,满足从栈底到栈顶的下标对应的数组 heightheight 中的元素递减。从左到右遍历数组,遍历到下标 ii 时,如果栈内至少有两个元素,记栈顶元素为 toptop, toptop 的下面一个元素是 leftleft,则一定有 height[left]height[top]height[left] \geq height[top]。如果 height[i]>height[top]height[i]>height[top],则得到一个可以接雨水的区域,该区域的宽度是 ileft1i−left−1,高度是 min(height[left],height[i])height[top]min(height[left],height[i])−height[top],根据宽度和高度即可计算得到该区域能接的雨水量。

具体的代码如下:

#include<iostream>
#include<vector>
#include<stack>
using namespace std;
#define Max(a,b) (a>b)?a:b
#define Min(a,b) (a<b)?a:b
int dp(vector<int>& height,int n)
{
  vector<int> leftMax(n,0);
  leftMax[0]=height[0];
  for(int i=1;i<n;i++)
  {
    leftMax[i]=Max(leftMax[i-1],height[i]);
  }
  vector<int> rightMax(n,0);
  rightMax[n-1]=height[n-1];
  for(int i=n-2;i>=0;i--)
  {
    rightMax[i]=Max(leftMax[i+1],height[i]);
  }
  int sum=0;
  for(int i=0;i<n;i++)
  {
    int tmp=Min(leftMax[i],rightMax[i]);
    sum+=(height[i]-tmp);
  }
  return sum;
}
int mono(vector<int>& height,int n)
{
  stack<int> S;
  S.push(0);
  int sum=0;
  for(int i=1;i<n;i++)
  {
    if(height[i]>height[S.top()])
    {
      while(height[S.top()]<height[i])
      {
        int cur=S.top();
        S.pop();
        if(S.empty())
        {
          break;
        }
        sum+=(Min(height[S.top()],height[i])-height[cur])*(i-S.top()-1);
      }
    }
    S.push(i);
  }
  return sum;
}
int main() {
  int n;
  cin>>n;
  vector<int> height(n);
  for(int i=0;i<n;i++)
  {
    cin>>height[i];
  }
  cout<<dp(height,n)<<endl;
  cout<<mono(height,n)<<endl;
  return 0;
}