首發於 樊浩柏科學院
自如寓打算門口用磚頭圍立一個蓄水池子,從上面看凹凸不平,凹的地方會有積水。那若是用數字表明每一個磚頭的高度,就造成一個二維數據(如示例),請問這個池子能存儲多少單位的水?
php
例如二維數組爲:html
9 9 9 9
3 0 0 9
7 8 2 6
時,答案是中間的 0,0 位置能夠存儲 2(由於其外面最低是 2)個單位的水,所以答案爲 2 + 2 = 4。git
示例:
輸入:[1 1 1 1,1 0 0 1,1 1 1 1]
輸出:2
輸入:[12 11 12 0 13,12 9 8 12 12,13 10 0 3 15,19 4 4 7 15,19 4 3 0 15,12 13 10 15 13]
輸出:58github
這道題是全部題中困惑我時間最長的題,一開始思惟禁錮在想直接經過找到每塊磚的四周有效最低磚高度 $H_{min}$,而後這塊磚所剩的水爲 $w[i][j] = H_{min}-h[i][j]$($h[i][j]$ 爲磚的高度,i 和 j 爲磚的位置座標),所以蓄水池能蓄下的水爲 $\sum_{i=1}^n\sum_{j=1}^n w[i][j]$。通過一番嘗試,發現尋找某塊磚四周最低有效磚邏輯比較複雜,且不易理解,又嘗試過使用回溯算法尋找出池子中的全部連通圖,可是也未有果。面試
最後,發現基礎平臺一位同窗的實現思路很清晰,我認爲他的實現是最合適的,因此研究了一下。該實現中機智地採用逆向思惟,首先往池子注滿水(最高磚的高度),而後再經過條件斷定每塊磚是否須要進行漏水,一直到沒有磚須要進行漏水操做。算法
實現思路以下:編程
算法流程圖示以下:數組
實現的類結構以下,特殊的方法已提取出,並將一一詳細說明。this
<?php class Pool { public $gridArray = array(); public $maxHeight = 0; public $row = 0; public $col = 0; public function __construct(array $data) { $this->row = count($data); $this->col = count($data[0]); foreach ($data as $row => $rowArray) { foreach ($rowArray as $col => $height) { $height = (int)$height; $this->gridArray[$row][$col]['height'] = $height; $this->gridArray[$row][$col]['water'] = 0; //獲取最高磚的高度 if ($this->maxHeight < $height) { $this->maxHeight = $height; } } } } //判斷是不是水池邊界 public function isBorder($row, $col) { if ($row == 0 || $row == $this->row - 1 || $col == 0 || $col == $this->col - 1 ) { return true; } return false; } public function run() { $this->addWater(); while ($this->removeWater()) ; return $this->collect(); } }
注水操做:編碼
public function addWater() { foreach ($this->gridArray as $row => $rowArray) { foreach ($rowArray as $col => $grid) { if (!$this->isBorder($row, $col)) { $this->gridArray[$row][$col]['water'] = $this->maxHeight - $this->gridArray[$row][$col]['height']; } } } }
漏水操做:
public function removeWater() { foreach ($this->gridArray as $row => $rowArray) { foreach ($rowArray as $col => $grid) { if ($this->canRemove($row, $col)) { return true; } } } return false; }
漏水條件實現以下:
public function canRemove($row, $col) { $can = false; if ($this->gridArray[$row][$col]['water'] > 0) { //上 if ($this->gridArray[$row][$col]['water'] + $this->gridArray[$row][$col]['height'] > $this->gridArray[$row - 1][$col]['water'] + $this->gridArray[$row - 1][$col]['height']) { $this->gridArray[$row][$col]['water'] = $this->gridArray[$row - 1][$col]['water'] + $this->gridArray[$row - 1][$col]['height'] - $this->gridArray[$row][$col]['height']; if ($this->gridArray[$row][$col]['water'] < 0) { $this->gridArray[$row][$col]['water'] = 0; } $can = true; } //右 if ($this->gridArray[$row][$col]['water'] + $this->gridArray[$row][$col]['height'] > $this->gridArray[$row][$col + 1]['water'] + $this->gridArray[$row][$col + 1]['height']) { $this->gridArray[$row][$col]['water'] = $this->gridArray[$row][$col + 1]['water'] + $this->gridArray[$row][$col + 1]['height'] - $this->gridArray[$row][$col]['height']; if ($this->gridArray[$row][$col]['water'] < 0) { $this->gridArray[$row][$col]['water'] = 0; } $can = true; } //下 if ($this->gridArray[$row][$col]['water'] + $this->gridArray[$row][$col]['height'] > $this->gridArray[$row + 1][$col]['water'] + $this->gridArray[$row + 1][$col]['height']) { $this->gridArray[$row][$col]['water'] = $this->gridArray[$row + 1][$col]['water'] + $this->gridArray[$row + 1][$col]['height'] - $this->gridArray[$row][$col]['height']; if ($this->gridArray[$row][$col]['water'] < 0) { $this->gridArray[$row][$col]['water'] = 0; } $can = true; } //左 if ($this->gridArray[$row][$col]['water'] + $this->gridArray[$row][$col]['height'] > $this->gridArray[$row][$col - 1]['water'] + $this->gridArray[$row][$col - 1]['height']) { $this->gridArray[$row][$col]['water'] = $this->gridArray[$row][$col - 1]['water'] + $this->gridArray[$row][$col - 1]['height'] - $this->gridArray[$row][$col]['height']; if ($this->gridArray[$row][$col]['water'] < 0) { $this->gridArray[$row][$col]['water'] = 0; } $can = true; } } return $can; }
持續漏水操做:
public function run() { while ($this->removeWater()) ; }
求和磚的盛水量:
public function collect() { $sum = 0; foreach ($this->gridArray as $row => $rowArray) { foreach ($rowArray as $col => $grid) { $sum += $grid['water']; } } return $sum; }
接收標準輸入處理並輸出結果:
$filter = function ($value) { return explode(' ', $value); }; $pool = new Pool(array_map($filter, explode(',', $input))); echo $pool->run(), PHP_EOL;
Twitter 以前曾經出過相似蓄水池的筆試題,只不過本題是立體水池(二維數組),Twitter 蓄水池筆試題是平面水池(一維數組),解題複雜度也就下降了,固然 Twitter 蓄水池筆試題也能夠採用本題的思想來實現,可是時間複雜度爲 $O(n^2)$,採用 個人Twitter技術面試失敗了 的實現時間複雜度爲 $O(n)$。
實現思路以下:
具體實現,請直接參考 CuGBabyBeaR 文章。
本題的蓄水池問題,若是理解了問題本質並逆向思惟,將尋找某塊磚四周最低有效磚高度(尋找有效磚涉及到邊界擴散)轉化爲判斷某塊磚是否須要漏水條件,那麼問題就簡化不少了,那後續編碼也就很容易實現了,本文算法的時間複雜度爲 $O(n^3)$。
相關文章 »
(2017-12-05)