当青训营遇上码上掘金-攒青豆

49 阅读2分钟

这道题就是力扣的经典题目“接雨水”了,可以用多种解法来解,这里介绍思路比较简单的dp解法,供大家学习。

首先我们需要考虑到是容器盛装的青豆约束条件,即每一列能装的青豆的状态转移方程。 image.png 在上图中以第三列为例,其能盛装的青豆取决于其自身高度和左右两边比它高的边的高度。 在这个例子中,第三列能保存的青豆=min(5,4)-2=2。

我们使用rightmax[]和leftmax[]分别保存右边和左边最高的柱子高度。
从而我们可以得到:
  当左右都存在高于当前列的高度的柱子时,res[i] = min(leftmax[i],right[i])-height[i])

至于剩余情况,即左右不同时存在高于当前柱子高度的情况,我们从图中也可以发现可以盛装的青豆数量是为0的。但是如果按照上述方程进行计算,结果可能得到负数,对此我们需要改造一下我们的状态转移方程:
  res[i] = max(0, min(rightmax[i], leftmax[i]) - height[i]);

另外在求rightmax[]和leftmax[]时,对于两边边界柱子也要进行特殊处理,在本题中不妨设左边界柱子的rightmax等于其自身,右边界同理,即:

    int len = height.size();
    leftmax[0] = height[0];
    rightmax[len - 1] = height[len - 1];

思路梳理完毕,我们对所给的样例height = [5,0,2,1,4,0,1,0,3]进行模拟,可得:
  leftmax = [5, 5, 5, 5, 5, 5, 5, 5, 5]
  rightmax = [5, 4, 4, 4, 4, 3, 3, 3, 3]
  res = [0, 4, 2, 3, 0, 3, 2, 3, 0]

最终结果为:17,即为所求。

完整代码如下:

#include <bits/stdc++.h>
using namespace std;

int AccuGreenBeans(vector<int> height)
{
    int len = height.size();
    vector<int> leftmax(len);
    vector<int> rightmax(len);
    leftmax[0] = height[0];
    rightmax[len - 1] = height[len - 1];
    for (int i = 1; i < len; i++)
    {
        leftmax[i] = max(leftmax[i - 1], height[i]);
    }
    for (int i = len - 2; i >= 0; i--)
    {
        rightmax[i] = max(rightmax[i + 1], height[i]);
    }
    int res = 0;
    for (int i = 0; i < len; i++)
    {
        res += max(0, min(rightmax[i], leftmax[i]) - height[i]);
    }
    return res;
}

int main()
{
    vector<int> height = {5, 0, 2, 1, 4, 0, 1, 0, 3};
    int res = AccuGreenBeans(height);
    cout << res << endl;
    return 0;
}