BM94 接雨水问题(python)

题目

题目链接

给定一个整形数组arr,其中所有值非负
将这个数组看作一个柱子高度图,数组以外的区域高度视为0,求按此排列的柱子下雨之后能接多少雨水

在这里插入图片描述

解法1

对每一列,该列能存的雨水高度=min(左边最高的柱子,右边最高的柱子)-该列的高度

例如上图中 arr[1]=1,左边最高的柱子为 arr[0]=3,右边最高的柱子为 arr[3]=5,则该列能存的雨水高度为 min(arr[0],arr[3]) - arr[1] = 3-1 = 2

dp

因此可以开两个dp数组,分别记录当前位置左边最高的柱子、当前位置右边最高的柱子

从左往右遍历一次,判断当前位置的柱子是否比 前一个位置的左边最高的柱子高,若当前的柱子高,dp值记录当前下标;否则,仍然记录 前一个位置的dp值

从右往左遍历一次,进行类似的操作,记录每个位置右边最高的柱子

最后,从左往右遍历这两个dp数组,计算每列能存的雨水,累加

代码

class Solution:
    def maxWater(self, arr):
        water = 0
        # dp记录每个位置左边最高的柱子和右边最高的柱子
        dp = [[-1, -1] for i in range(len(arr))]

        dp[0][0] = 0
        for i in range(1, len(arr)):
            dp[i][0] = dp[i - 1][0] if arr[i] <= arr[dp[i - 1][0]] else i

        dp[-1][1] = len(arr) - 1
        for j in range(len(arr) - 2, -1, -1):
            dp[j][1] = dp[j + 1][1] if arr[j] <= arr[dp[j + 1][1]] else j

        for k in range(len(arr)):
            water += min(arr[dp[k][0]], arr[dp[k][1]]) - arr[k]

        return water

解法2

思路:每发现一个u型凹槽(高,低,高),计算该凹槽中的水量

对一个u型凹槽,水量:宽度 *(min(左高度,右高度) - 底高度)

计算后,把该凹槽填平,继续寻找凹槽

例,有数组[4,2,0,3,2,5],画图理解整个过程:
在这里插入图片描述

单调栈

想要及时找到每一个u型凹槽,可以维护一个单调递减的栈;由于本题中需要考虑凹槽的宽度,因此栈中应当记录的是下标

若出现比栈顶元素大的元素,则(次栈顶,栈顶,当前元素)一定构成一个凹槽,计算该凹槽中的水量,并弹出栈顶元素

注意,弹出后,栈顶元素可能仍然比当前元素小,此时仍然构成u型槽,因此需要循环计算水量并弹出栈顶元素;直到栈中没有比当前元素小的元素,再把当前元素入栈

遍历整个数组,不断维护单调栈,即可求得总水量

代码

class Solution:  # 单调栈法
    def maxWater(self, arr):
        stack = []
        water = 0
        for i in range(len(arr)):
            if not stack or arr[i] <= arr[stack[-1]]:
                pass
            else:
                while stack and arr[i] > arr[stack[-1]]:
                    if len(stack) > 1:
                        width = i - stack[-2] - 1
                        water += width * (min(arr[stack[-2]], arr[i]) - arr[stack[-1]])
                    stack.pop()
            stack.append(i)
        return water

参考链接

https://blog.nowcoder.net/n/7b2ff3cdc8a44d9084288a6682eb1531?f=comment

https://blog.csdn.net/weixin_42265382/article/details/120910200

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值