大家好!
動態規劃題目是總結的比較完整了。下面是自從和大家刷開題總結的動態規劃解題方法。
今年全國夏天雨是真的多,突然想到今年北京的夏天也不像往年那麼熱。不知不覺就穩穩地度過了夏天來到秋天。
恰巧前幾天有一個粉絲問到了我,網上接雨水的解決總是感覺有點混亂,能不能用動態規劃解決。
今早北京大雨,借用大雨的感受,想了想接雨水問題,依然用長圖一步一步說明!
背景
先來看看題目,這個題目應該是很多人都已經遇到過了,因為它的題號是42,屬於一個比較非常靠前的題目。
同時也屬於一個非常經典的演算法問題。
我們們今天的題目解決不做暴力法、也不做雙指標,就用動態規劃很清晰的進行說明。
看下圖,12根柱子的圍欄,接了6個單位的雨水。
從上圖很顯然能看到一點:
如果想要接住雨水。那麼,決定雨量的多少在於「左邊的柱子高度」、「右邊的柱子高度」以及「自身柱子的高度」。
比如說,中間第 5 格雨水量為2,就是決定於左右側柱子的較小值-本身柱子高度(0)而得到的。
注意:左右側的高度,指的是能圍住雨水的柱子,而不是緊挨著的左右側的柱子。
左側最高柱子:2
右側最高柱子:3
自身柱子高度:0
雨水量 = min(左側最高柱子, 右側最高柱子) - 自身柱子高度 = 2
因此,需要定義兩個陣列,分別來存放相對於當前位置左側和右側柱子的最大高度。
最後,取左右側柱子最小值-自身柱子高度=雨水量。
思路
就用leetcode官方給的案例來進行一步一步解決。
柱子高度為:height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1],再把圖拿過來!
現在,定義兩個陣列,分別命名為 left_max_height
和 right_max_height
,存放相對於當前位置左側和右側柱子的最大高度。
先來定義第一個陣列left_max_height
,存放左側最高柱子高度:
初始化:(因為左側沒有柱子,所以位置 0 左側最大高度為 0)
left_max_height[0] = 0
動態方程:
left_max_height[i]=max(height[i-1], left_max_height[i-1])
下面還是用長圖一步一步來進行說明:【點選高清顯示】
下面開始定義第二個陣列,存放右側最高柱子高度。右側最高度從最右側開始進行計算。
初始化:(因為最後一個位置由側沒有柱子,所以位置 11 右側最大高度為 0)
right_max_height[11] = 0
動態方程:
right_max_height[j]=max(height[j+1], right_max_height[j+1])
還是用長圖一步一步來進行說明:【點選高清顯示】
現在,當前位置的左側和右側柱子的最大高度陣列計算完成後,下面就計算接水量。
雨水量 = min(左側最高柱子, 右側最高柱子) - 自身柱子高度 = 2
準備好三個陣列:
上圖中,height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1],為了看起來清晰,可以參考最開始圖示。
最大單位水量=左右取最小值-本身的高度
計算方式:
water=min(left_max_height[i], right_max_height[i])-height[i])
max_water=water>=0?water:0
上述需要注意一點,如果左右取最小值-本身的高度<0
,說明當前本身柱子是凸出來左右側最高值的,比如說位置 1:
所以說,位置 1 的計算結果為負數,需要強制轉為 0。
程式碼
class Solution(object):
def trap(self, height):
size = len(height)
# 小於等於 2 的時候,是接不住雨水的
if size <= 2:
return 0
# 左邊相對於當前位置的最大高度
left_max_height = [0 for _ in range(size)]
# 右邊相對於當前位置的最大高度
right_max_height = [0 for _ in range(size)]
# 當前位置接雨水最大高度
max_water = [0 for _ in range(size)]
# 初始化 left_max_height, 第 0 個位置初始化為 0
for i in range(1, size):
left_max_height[i] = max(height[i-1], left_max_height[i-1])
# 初始化 right_max_height, 第 size-1 個位置初始化為 0
for j in range(1, size):
right_max_height[size-j-1] = max(height[size-j], right_max_height[size-j])
# 最大水量
for k in range(1, size):
max_water[k] = (min(left_max_height[k], right_max_height[k])-height[k] if min(left_max_height[k], right_max_height[k])-height[k]>=0 else 0)
# 累計求單位水量
waters = 0
for z in range(1, size):
waters += max_water[z]
return waters
if __name__ == '__main__':
s = Solution()
print(s.trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]))
這個題目總體來說,使用動態規劃還是不容易想到的。尤其是上面兩個陣列使用了兩次動態規劃的過程。
接雨水問題還可以使用暴力解法和雙指標解決,雙指標可以試試,至於暴力。。心裡有就好了哈哈。。
多餘的一句
邊工作邊帶大家刷題確實是有點慢了,很抱歉!
不過,一定會堅持下去,也一定會把 LeetCode 各型別題目進行詳細總結,做到大家想檢視的時候隨時能查到詳細 LeetCode 資料的地方。
另外,方便的話也在我的github
? 加顆星,它是我持續輸出最大最大的動力,感謝大家!