攒青豆(DP)

64 阅读3分钟

当青训营遇上码上掘金

1.原题

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

2.思路

力扣上类似的接雨水问题,算的上有点小难度。这道题有多种解法,什么动态规划,单调栈···这里我用的是动态规划。第一眼看到这个题目,我想到的是双指针,但是,我发现要考虑几种情况,有点脑瓜疼,没有AC。便开始尝试其他解法。(虽然后面写出来了,但是我觉得dp更好)

从题目中可以看出,最左边的柱子是一定要比右边的大的,最右边的柱子要比左边的大,,满足这两个条件。不然接不了豆子,比如[1,2,3]。而换作[3,2,3]它就可以。 除去最左和最右的柱子,来考虑中间的柱子。我们可以发现,豆子的高度取决于柱子左边最高的柱子和右边最高的柱子哪个最矮的柱子青豆值就等于最左值与最右值中较小的那个于柱子本身的差值。而最左和最右值的得到,需要点方法。比如[5,0,2,1,4]中,0左边是5,而右边最高的是4。对于2,1也是如此。换做是[5,0,6,1,4],0左边是5,右边则是6。而1左边则是6,右边是4。说到这,其实可以想到通过动态规划的方法把状态转移方程给写出来了。 中间的柱子的最左和最右值可以从前面的柱子中转移过来。

3.实现

  1. 设柱子的数组为height[n],n为数组长度。我们可以设两个数组,一个记录每个柱子的最左值,一个记录最右值。left[n]right[n]
  2. 对于left[n],第一个柱子没有最左值,得到left[0]=height[0]。而其他柱子从1开始,即满足left[i]=max(height[i-1],left[i]),1 <= i<= n-1
    left[0]=height[0];
    for(int i=1;i<n;++i){
        left[i]=max(height[i],left[i-1]);
    }
  1. 对于right[n],最后一个柱子没有最右值,得到right[n-1]=height[n-1]。而其他柱子,最右值是得从最右边进行迭代,从n-2开始。即满足right[i]=max(height[i],right[i+1]),0 <= i<= n-2
    right[n-1]=height[n-1];
    for(int i=n-2;i>=0;--i){
        right[i]=max(height[i],right[i+1]);
    }
  1. 算出数组之后,就要对每个列的青豆进行计算了。可以推出状态转移方程res[i]=min(left[i],right[i])-height[i],这里只需要计算青豆的总量,不用开数组res[],开个int变量res进行累加即可。

下图是力扣的官方题解的图,可以参考:

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<int>height={5,0,2,1,4,0,1,0,3};
    int n=height.size();
    int left[n],right[n];
    int res=0;

    left[0]=height[0];
    for(int i=1;i<n;++i){
        left[i]=max(height[i],left[i-1]);
    }

    right[n-1]=height[n-1];
    for(int i=n-2;i>=0;--i){
        right[i]=max(height[i],right[i+1]);
    }

    for(int i=0;i<n;++i){
        res+=min(left[i],right[i])-height[i];
    }
    cout<<res;
    return 0;
}

写完发现和力扣的题解很类似,哈哈,好像通解都是这样,我只不过走了别人走过的路罢了。

有错误,请指正,谢谢!