【算法】雨水问题(新增解析和答案)

415 阅读3分钟

yushui.png

题源:

这是我在某次比赛中的比赛题,具体什么比赛忘了。。。

题意:

如图有一堆方块,现在下雨了,方块不会被冲走,方块与方块之间的凹槽里会接住雨水,现在雨下的足够大,保证可以把所有的凹槽填满,不会有雨水外溅,问可以接到多少雨水(单位:块)

输入方块的数量的数组nn[i]代表第i列方块的数量。

输出雨水所占方块数

样例:

输入1,3,2,0,2,1,2

输出3

解释:

4列 和 第6列 会一共存雨水 3个方块,如图

雨水-01——.png

解析:

首先,要明白的是两个高的柱子中间会积水 这里分三种情况,我举几个例子

  1. 理想情况 2,0,1,3 这种情况下是理想的情况,23 是高柱子,中间积水量是高柱子的 偏小值(2) 减去中间每列的块数之和——— min(2,3)=22-0 + 2-1=3 ,这种情况需要找出高柱子,然后减去中间每列的块,然后加和。

高柱子怎么找呢?两边的数小于中间的数就行了?看着好像是的,但情况没那么简单,我们看下一种情况

  1. 高柱子重复 2,2,2,1,3,3,3 这种情况有重复的高柱子,我的思路是将连续的高柱子看为一个整体,简而言之,就是连续的高柱子是没有意义的,可以直接变成一个高柱子——— 2,1,3 ,因为一样高的高柱子并不存水。

  2. 高柱子判断条件的特例 9,0,2,0,6,1,8 这种情况下看似 9268 都是高柱子,但是实际上呢?我们看图,可以发现2和6并没有起到高柱子那种把水分开的作用

雨水特例-03.png

答案:

(可在评论区说出你的思路,大家一起讨论,我的答案也不一定对,大家一起学习)

这里我拿一个数组 1,4,4,2,9,0,1,0,6,1,8 来说明我的思路

//自定义的一个类,用来记录这一列的块数和在数组中的位置
class High(var value: Int = 0, var index: Int = 0)

fun rain(intArray: IntArray):Int {
    var count = 0
    val highList = ArrayList<High>()
    for (i in intArray.indices) {
        //先判断当前数是不是在尖尖上(左右都<=他,就表示他在尖尖上,比如21的2,131的3,444的4,当然了最左边和最右边的数我默认取了0作为边界值)
        if ((intArray[i] >= if (i == 0) 0 else intArray[i - 1]) && intArray[i] >= if (i == intArray.lastIndex) 0 else intArray[i + 1]) {
            //当他在尖尖上时,我需要判断上一个高柱子在高柱子list里面是不是比两边的都要矮(list里面是9,1的时候,我判断到6,就会看看上一个数1,他比前面的数以及刚得出的高柱子都要矮,所以要替换他)
            if (if (highList.size > 1) highList.last().value >= highList[highList.lastIndex - 1].value && highList.last().value >= intArray[i] else true) {
                highList.add(High(intArray[i], i))
            } else {
                highList[highList.lastIndex] = High(intArray[i], i)
            }
        }
    }
    
    //最后遍历并得到最终的雨水总数
    var nowHighValue: Int
    for (j in highList.indices) {
        if (j != highList.lastIndex) {
            nowHighValue = min(highList[j].value, highList[j + 1].value)
            for (k in highList[j].index + 1 until highList[j + 1].index) {
                count += nowHighValue - intArray[k]
            }
        }
    }
    return count
}