[LeetCode] Trapping Rain Water II 收集雨水之二
Given an m x n
matrix of positive integers representing the height of each unit cell in a 2D elevation map, compute the volume of water it is able to trap after raining.
Note:
Both m and n are less than 110. The height of each unit cell is greater than 0 and is less than 20,000.
Example:
Given the following 3x6 height map: [ [1,4,3,1,3,2], [3,2,1,3,2,4], [2,3,3,2,3,1] ] Return 4.
The above image represents the elevation map [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]]
before the rain.
After the rain, water are trapped between the blocks. The total volume of water trapped is 4.
這道題是之前那道Trapping Rain Water的拓展,由2D變3D了,感覺很叼。但其實解法跟之前的完全不同了,之前那道題由於是二維的,我們可以用雙指標來做,而這道三維的,我們需要用BFS來做,解法思路很巧妙,下面我們就以題目中的例子來進行分析講解,多圖預警,手機流量黨慎入:
首先我們應該能分析出,能裝水的底面肯定不能在邊界上,因為邊界上的點無法封閉,那麼所有邊界上的點都可以加入queue,當作BFS的啟動點,同時我們需要一個二維陣列來標記訪問過的點,訪問過的點我們用紅色來表示,那麼如下圖所示:
我們再想想,怎麼樣可以成功的裝進去水呢,是不是周圍的高度都應該比當前的高度高,形成一個凹槽才能裝水,而且裝水量取決於周圍最小的那個高度,有點像木桶原理的感覺,那麼為了模擬這種方法,我們採用模擬海平面上升的方法來做,我們維護一個海平面高度mx,初始化為最小值,從1開始往上升,那麼我們BFS遍歷的時候就需要從高度最小的格子開始遍歷,那麼我們的queue就不能使用普通隊列了,而是使用優先順序佇列,將高度小的放在隊首,最先取出,這樣我們就可以遍歷高度為1的三個格子,用綠色標記出來了,如下圖所示:
如上圖所示,向周圍BFS搜尋的條件是不能越界,且周圍格子未被訪問,那麼可以看出上面的第一個和最後一個綠格子無法進一步搜尋,只有第一行中間那個綠格子可以搜尋,其周圍有一個灰格子未被訪問過,將其加入優先佇列queue中,然後標記為紅色,如下圖所示:
那麼優先佇列queue中高度為1的格子遍歷完了,此時海平面上升1,變為2,此時我們遍歷優先佇列queue中高度為2的格子,有3個,如下圖綠色標記所示:
我們發現這三個綠格子周圍的格子均已被訪問過了,所以不做任何操作,海平面繼續上升,變為4,遍歷所有高度為4的格子,如下圖綠色標記所示:
由於我們沒有特別宣告高度相同的格子在優先佇列queue中的順序,所以應該是隨機的,其實誰先遍歷到都一樣,對結果沒啥影響,我們就假設第一行的兩個綠格子先遍歷到,那麼那麼周圍各有一個灰格子可以遍歷,這兩個灰格子比海平面低了,可以存水了,把存水量算出來加入結果res中,如下圖所示:
上圖中這兩個遍歷到的藍格子會被加入優先佇列queue中,由於它們的高度小,所以下一次從優先佇列queue中取格子時,它們會被優先遍歷到,那麼左邊的那個藍格子進行BFS搜尋,就會遍歷到其左邊的那個灰格子,由於其高度小於海平面,也可以存水,將存水量算出來加入結果res中,如下圖所示:
等兩個綠格子遍歷結束了,它們會被標記為紅色,藍格子遍歷會先被標記紅色,然後加入優先佇列queue中,由於其周圍格子全變成紅色了,所有不會有任何操作,如下圖所示:
此時所有的格子都標記為紅色了,海平面繼續上升,繼續遍歷完優先佇列queue中的格子,不過已經不會對結果有任何影響了,因為所有的格子都已經訪問過了,此時等迴圈結束後返回res即可,參見程式碼如下:
class Solution { public: int trapRainWater(vector<vector<int>>& heightMap) { if (heightMap.empty()) return 0; int m = heightMap.size(), n = heightMap[0].size(), res = 0, mx = INT_MIN; priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; vector<vector<bool>> visited(m, vector<bool>(n, false)); vector<vector<int>> dir{{0,-1},{-1,0},{0,1},{1,0}}; for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (i == 0 || i == m - 1 || j == 0 || j == n - 1) { q.push({heightMap[i][j], i * n + j}); visited[i][j] = true; } } } while (!q.empty()) { auto t = q.top(); q.pop(); int h = t.first, r = t.second / n, c = t.second % n; mx = max(mx, h); for (int i = 0; i < dir.size(); ++i) { int x = r + dir[i][0], y = c + dir[i][1]; if (x < 0 || x >= m || y < 0 || y >= n || visited[x][y]) continue; visited[x][y] = true; if (heightMap[x][y] < mx) res += mx - heightMap[x][y]; q.push({heightMap[x][y], x * n + y}); } } return res; } };
類似題目:
參考資料: