如果有一座高度是10級台階的樓梯,從下往上走,每跨一步只能向上1級或者2級台階。
要求用程式來求出一共有多少種走法。
如果每次走1級台階,一共走10步,這是其中一種走法。我們可以簡寫成 1,1,1,1,1,1,1,1,1,1。
如果每次走2級台階,一共走5步,這是另一種走法。我們可以簡寫成 2,2,2,2,2。
當然,除此之外,還有很多很多種走法。
我們可以利用排列組合的思想進行暴力列舉,寫乙個多層巢狀迴圈遍歷出所有的可能性。每遍歷出乙個組合,讓計數器加1。這個方法的時間複雜度是指數級的,一般不推薦使用。
這裡我們可以使用動態規劃解決問題。
動態規劃(dynamic programming)是一種分階段求解決策問題的數學思想。不僅可以用於程式設計領域,也可以用於管理學、經濟學、生物學。
動態規劃總結起來就是:大事化小,小事化了。
上面的題目,假設你只差最後一步就走到10級台階,這時候會出現幾種情況呢?2種,因為每一步只能走1級或者2級台階,第一種就是從9級走到10級,第二種就是從8級走到10級。所以不管前面的過程,想要走到10級,最後一步必然是從8級或者9級開始。
我們接著分析,如果我們已知從0到9級台階的走法有x種,0到8級台階的走法有y種,那麼0到10級台階的走法有多少種?
10級台階的所有走法可以根據最後一步的不同而分成兩部分,第一部分的最後一步是從9級到10級,這部分的走法數量和9級台階的走法數量是相等的,也就是x。第二部分的最後一步是從8級到10級,這部分的走法數量和8級台階的走法屬性是相等的,也就是y。這兩部分相加,總的走法數量是x+y。
思路如下:
因此,得出結論:從0走到10級台階的走法數量=從0走到8級的走法數量+從0走到9級的走法數量
我們把10級台階的走法數量寫成f(10),那麼f(10)=f(9)+f(8),然後我們只有求出f(8)和f(9)即可。
根據前面的思路可以推斷出:
f(9)=f(8)+f(7)
f(8)=f(7)+f(6)
上述過程,我們把乙個複雜的問題分階段簡化,逐步簡化成簡單的問題,這就是動態規劃。
當只有1級台階和2級台階的時候,分別有1和2種走法,因此我們可以歸納出下面的公式:
f(1)=1
f(2)=2
f(n)=f(n-1)+f(n-2) (n>=3)
動態規劃的三個重要概念:最優子結構、邊界和狀態轉移公式。
最優子結構:前面f(8)和f(9)是f(10)的最優子結構。
邊界:當只有1級或2級台階時,可以直接得出結果,無需繼續簡化,稱f(1)和f(2)是問題的邊界。如果乙個問題沒有邊界,將永遠無法得到有限的結果。
前面這個部分叫問題建模,下面要進行比較麻煩的階段:求解問題。
根據上面的公式,我們很容易地寫出遞迴**
int getclimbingways(int n) if(n == 1) if(n == 2) return getclimbingways(n-1)+getclimbingways(n-2)}
此種演算法的時間複雜度計算如下圖
是一顆二叉樹,高度是n-1,節點個數接近2的n-1次方。所以時間複雜度近似地看做是o(2^n)。
上述實現方式雖然能夠解決問題,但是演算法效率比較低,根據上面的遞迴圖,有很多相同的引數被重複計算了,越往下越多
如圖所示,相同的顏色代表了方法被傳入相同的引數。
我們可以採取快取的方式將不同引數的計算結果存入雜湊表來避免重複計算,這種演算法又被稱為備忘錄演算法。
int getclimbingways(int n,hashmap maps) if(n == 1) if(n == 2) if(maps.contains(n))else}
集合maps是乙個備忘錄,當每次需要計算f(n)的時候,會首先從maps中尋找匹配元素。如果maps中存在,就直接返回結果,如果maps中不存在,就計算出結果,存入備忘錄中。
此演算法的時間和空間複雜度都是o(n)
以上還不能算是真正的動態規劃實現,還有優化的空間。
我們可以把思路逆過來,之前是對f(n)自頂向下做遞迴運算,也可以自底向上用迭代的方式推導出結果。
f(1)=1,f(2)=2 我們已經知道這個結果。
那麼f(3)依賴於f(1)和f(2),有f(3)=f(1)+f(2)=3
同理,f(4)依賴於f(3)和f(2),有f(4)=f(3)+f(2)=5
由此可見,每一次迭代過程中,只要保留前之前的兩個狀態,就可以推導出新的狀態,而不需要像備忘錄演算法那樣保留全部的子狀態。
真正的動態規劃求解:
int getclimbingways(int n) if(n == 1) if(n == 2) int a = 1; int b = 2; int temp = 0; for (int i=3;i<=n;i++) return temp;}
程式從 i=3 開始迭代,一直到 i=n 結束。每一次迭代,都會計算出多一級台階的走法數量。迭代過程中只需保留兩個臨時變數a和b,分別代表了上一次和上上次迭代的結果。 為了便於理解,引入了temp變數,temp代表了當前迭代的結果值。
此演算法的時間複雜度是o(n),空間複雜度是o(1)。
這就是動態規劃,利用簡潔的自底向上的遞推方式實現了時間和空間上的最優化。
這道上樓梯的題目是動態規劃領域中最簡單的問題,希望對你了解和入門動態規劃有所幫助。
動態規劃走樓梯 分治 動態規劃 回溯 貪心一鍋燉
觀感度 五顆星 口味 東北一鍋出 本文已收錄在githubgithub.com geekhyt,感謝star。初學者一聽到演算法思想,就會覺得它們高深莫測,只能望而卻步。但如果你看過 事實 這本書,你就不會被大腦中的慣性思維所影響。只要我們理解演算法思想的關鍵點,多做題練習並加深理解記憶。其實演算法...
很特別的乙個動態規劃入門教程
通過金礦模型介紹動態規劃 附 首先我們思考乙個問題,如何用最少的硬幣揍夠i元 i 11 為什麼要這麼問呢?兩個原因 1 當我們遇到乙個大問題時,總是習慣把問題的規模變小,這樣便於分析討論。2.這個規模變小後的問題和原來的問題是同質的,除了規模變小,其它的都是一樣的,本質上它還是同乙個問題規模變小後的...
乙個簡單的動態規劃題
一直感覺 動態 規劃和排列好難的 乙個簡單的題目。開司,乙個整日遊手好閒 無所事事 混跡人生 軟弱無能 放縱慾望 毫無進取 嗯,實在是太多了,就不一一枚舉了。總之,他就是完美的符合了我們日常中對人渣這一詞的認識。不過他有這唯一,也是無敵般的特長,就是逆境求生 不論是什麼樣的逆境,他都可以翻盤。這不是...