最大接雨量(困难)
题干
给定n个非0整数表示框度为1的柱子的高度图,计算按此排序的柱子,下雨后能接多少的雨水
height = [0,1,0,2,1,0,1,3,2,1,2,1]
// 答案: 6
*题目分析
每根柱子的接水量就等于当前柱子左边最高的柱子
和当前柱子右边最高的柱子
两者高度相互比较取小者,接着用小者的高度减去当前的柱子高度
就得到当前柱子的接水量。计算每个柱子的接水量,相加之后就是最终的答案。
这题的关键在于计算的是每根柱子的接水量,而不是多根柱子的总共接水量。
例如:第一根柱子。高度为0,因为是第一个柱子因此左侧最高是自身,右侧最高为第8根(下标为7)柱子。
第6根柱子:高度是0,左边柱子最高为2,右边最高位3,取小者。再减去自身高度,得到接水量为:min(3,2)-0 = 2-0 = 2
解题步骤:
- 计算出每根柱子的左边最高者的高度(包括自身)
- 计算每根柱子的右边最高者的高度(包括自身)
- 根据最值计算每根柱子的接水量,累加得到最终结果。
注:计算过程中必须吧自身加入对比会有偏差
-
1 计算每根柱子的左边最高者的高度(前缀最值)
从左向右遍历,如果是第一个那么最大值就是自身。从第二个开始,比较自身与前一位的最大值,取大者,实现如下:
// 算出前缀最值 let prevMax = [] height.forEach((v,k)=>{ if(k == 0){ prevMax[k] = v }else{ prevMax[k] = max(prevMax[k-1], v) } })
-
2 计算每根柱子的右边最高者的高度(后缀最值)
从右往左遍历(倒序遍历)。需要初始化一下数组元素,因为是倒序赋值。如果是最后一个,最大者就是自身,每个与下一位的最大值比较,取大者。具体实现如下:
// 算出后缀最值 let postMax = [] // 初始化数组 for(let i=0;i<height.length;i++){ postMax[i] = 0 } for(let i=height.length-1;i>=0;i--){ if(i==height.length-1){ postMax[i] = height[i] }else{ postMax[i] = max(postMax[i+1],height[i]) } }
-
3 计算和累加每根柱子的接水量
得到两边的最值之后即可计算每根柱子的接水量了;即:
ans += min(postMax[i],prevMax[i])-selfheight
代码编写:
function reciveRain(){
/* 给定n个非0整数表示框度为1的柱子的高度图,计算按此排序的柱子,下雨后能接多少的雨水 */
let height = [0,1,0,2,1,0,1,3,2,1,2,1] // 答案: 6
// 算出前缀最值
let prevMax = []
height.forEach((v,k)=>{
if(k == 0){
prevMax[k] = v
}else{
prevMax[k] = max(prevMax[k-1], v)
}
})
// 算出后缀最值
let postMax = []
// 初始化数组
for(let i=0;i<height.length;i++){
postMax[i] = 0
}
for(let i=height.length-1;i>=0;i--){
if(i==height.length-1){
postMax[i] = height[i]
}else{
postMax[i] = max(postMax[i+1],height[i])
}
}
// 遍历累加出答案
let ans = 0
height.forEach((v,i)=>{
ans += min(postMax[i],prevMax[i])-v
})
return "最高节水量为:"+ans
}
function min(a,b){
return a > b ? b : a
}
function max(a,b){
return a > b ? a : b
}
document.write(reciveRain()) // 输出: 最高接水量为:6
总结
解题的关键在于要知道如何计算单根柱子的接水量,以及能想到通过求单个柱子来计算总的接水量(局部最优解推出全局最优解)。这可能就需要通过多的联系和积累了。
至于前缀最值和后缀最值的计算,比较简单,可以看前两篇动态规划的前缀最值和后缀最值