動態規劃1(DP)總結

2021-10-04 05:48:06 字數 2883 閱讀 8020

遞迴是最簡單也是最直接的思路,分解小問題,然後得到最終問題的答案。但是遞迴**簡單,但是比較耗時。所以我們思路可以用遞迴, 但是出於**效能考慮,還是要提公升效率。這當然是後期的優化了。

動態規劃,回溯都是比較常用也比較常見的演算法。這篇來講動態規劃。

動態規劃的核心問題的最優解可以由子問題的最優解得到,那麼我們就可以出這些子問題的最優解,然後構造出最終解。如果在拆分時,乙個子問題重複出現,也就是說需要重複求解乙個問題幾次。那麼,就採用自底而上的思路。先求小解,儲存,在大問題用到此解時直接呼叫即可,省去了重複求解的時間。舉個常見的例子吧,歸併排序就是動態規劃的一種,最後的排序依賴於每乙個歸併時的有序,所以子問題的最優解就是每兩個歸併時都要有序,將所有的都歸併完成後自然就是乙個排序好的陣列。

從上述粗字可以推出,動態規劃的使用情景就是可以拆分為相關子問題。所以在使用動態規劃時要先分析問題的最優解是否跟子問題的最優解有關。

這裡面有兩種思路:自頂向下和自底向上,也就是從大問題到小問題和從小問題到大問題,是不是有點暈,不是子問題嗎?舉個例子就知道了。

有2、3、5三種錢幣,要湊成20,。我們將結果表示為f(20),f(20)有三種情況:f(20)=f(18)+1;f(20)=f(17)+1;f(20)=f(15)+1;也就是18的幣數+一張2,17的加3,15的加5。這樣繼續分解,這其實就是遞迴的子問題,但這樣有個不足就是,會有子問題的重複計算,18的幣數可能是15的幣數+一張3,17的可能是15的幣數+一張2,由於遞迴時兩個單獨計算,也就是說15的最少幣數情況只有乙個解,但是求了兩次。這種叫自頂而下。但是我們反過來,先求f(1),到15,然後記錄下來,那麼之後再有關15的幣數時直接使用即可,無需再求。這就是自底而上的好處。二者其實無本質區別,只是在子問題有重疊時,自底而上會減少計算從而提公升效率。

dp的使用邏輯,也就是**組織思路

這裡面子問題最優解容易理解,但是構造最優路線是什麼意思。專業點來說其實叫狀態轉移方程。從乙個狀態到另乙個狀態需要乙個選擇或轉換,那麼這個規則就是能得到最優解的關鍵,也就是子問題怎麼樣一步步走向最終問題。

我們還是以上面問題舉例。15的幣數怎麼走向20,也就是從15的狀態走向20的最終狀態,應該怎麼轉換。

實戰一下:

題目:有2、3、5三種貨幣,湊夠20的最少貨幣數是多少?

思路:最後是要f(20),按照上面分析的,我們從f(1)開始求。然後求出每個貨幣數的最少張數,到20就是20的幣數了。

因此我們需要乙個20的陣列來儲存結果。便於分析,我們dp[21],我們先用2來統計張數。

我們維持乙個21的陣列,動態的更新陣列,當新的狀態比舊的狀態小時,就替換原位置狀態。因為要更新為最少,所以我們開始以乙個不可能的大值初始化陣列,這樣就可以進行比較,這裡我以最大數初始化陣列,0處置0,因為其實0只有一種情況,0.

主要分析的是狀態轉移方程。我們先分析子問題,怎麼用2更新陣列。因為2一張就加2,所以我們在比較時,就是前兩個位置的張數+1張和當前位置比較,若是小於當前位置,說明此事的狀態更好,更新。從2開始,0處+1和當前的無限大比較,小,所以2處更新為1,3處為1處+1和當前狀態,無限大。4處為2處+1和當前,更新為2。直至到了20.此時2的情況已經全部求出,我們現在加入3,還是之前的思路,每加一張3就會大3,所以需要和三個位置前的比較。3處為0處+1和當前,更新為1,4處為1處+1和當面,無限大,5處為2處+1和當前,更新為2.也就是一張3和一張2是當前5的最小幣數。然後更新到最後為7.6個3和乙個2,是最少幣數。

加入5,同樣的道理。

**如下:

#include

using

namespace std;

intmain()

if(dp[aim]

==int_max)

//不存在

else

return0;

}

其實dp使用最多的情況時路徑規劃問題,也可以說是矩陣遍歷問題。這種問題在使用動態規劃時可以很好地改善效能。

直接實戰吧,劍指offer的禮物價值題很經典。

這是典型的動態規劃。因為每次都只能向右或向下走,所以這就是遞迴的方向。顯然,每個動態規劃問題都可以寫成這樣形式:f(i,j)=max(f(i-1,j),f(i,j-1))+value(i,j);然後遞迴解下去即可。從之前的分析可以看出,這樣會有大量的重複計算,所以我們還是用迴圈從下而上的解決問題,為此,我們需要乙個輔助陣列來訪問結果。

**獻上:

int

valuesum

(vector

int>>

& gift)

}return dp[rows]

[cols]

;}

再深入分析,第二行的狀態依賴於第一行,而第三行的依賴於第二行,也就是說最多隻需要維持一行的資料動態更新就可以了,這樣就可以對空間進行優化。我們在更新i,j處的資料時,需要知道左面和上面的數,如果用乙個一位陣列a[cols]來儲存,那麼i,j處前方的就是已經更新過得左值,後面就是未更新的上值。用**來解釋吧。

int

valuesum

(vector

int>>

& gift)

}return dp[cols-1]

;}

可以分析出動態陣列的時間效率基本是最小了,但是空間的優化是不一樣的,這個需要具體問題分析。

好了,今天就到這了。之後繼續寫。感興趣的可以繼續看之後的文章,會有動態規劃2,+貪心的講解。

動態規劃總結(1)

今日開始稍微學習動態規劃,前來總結下每日戰績 例如,給定三角形 2 3 4 6,5 7 4,1 8,3 自頂向下的最小路徑和為 11 即,2 3 5 1 11 class solution return f 0 之前做過不同路徑i 也是乙個動態規劃,這道題多了一些障礙物,但是依然是一道典型的動態規劃...

DP1 動態規劃概述

計數求最大值最小值 求存在性 確定狀態 簡單的說,就是解動態規劃時需要開乙個陣列,陣列的每個元素f i 或者f i j 代表什麼,類似解數學題中,xyz代表什麼一樣,具體分為下面兩個步驟 轉移方程 根據子問題定義直接得到 初始條件和邊界情況 初始條件一般都是a 0 a 1 這種,多看看邊界條件主要是...

DP動態規劃部分學習總結

這幾天學習動態規劃,我的理解是dp大致可分為2類,一種是自下而上 也叫遞推 另一種是自上而下。這兩種方法都可以達到目的。仔細來講動態規劃是解決多階段決策問題的一種方法。並且每一步得出的結論都講影響下一階段的結果將每一階段都需要取出最佳結果然後一步步下去得出最終答案。並且動態規劃要秉承最優性原理。而最...