這篇博文其實我是不想、也不敢寫的,因為自己還是半知半解,但又怕自己看了很久的東西和做題得來的體會以後給忘了,所以,還是寫下了。個人水平確實有限,若有錯誤的地方,歡迎指出!
參考了《演算法導論(原書第3版)》和網上的部落格。
一、動態規劃的原理
1、動態規劃的用處:
動態規劃與分治法相似,都是通過組合子問題的解來求解原問題。動態規劃通常是用來求解最優化問題(optimization problem).這類問題可以有很多個可行解,每個解都有乙個值,我們希望尋找最優值(最大值或者最小值)的解。我們稱這樣的解為問題的乙個最優解(oneoptimization solution)而不是(theoptimization solution)
2、設計動態規劃的步驟:
1)刻畫乙個最優解的結構特徵;
2)遞迴地定義最優解的值;
3)計算最優解的值,通常採用自底向上的方法;
4)利用計算出的線性構造乙個最優解。
運用動態規劃求解問題的兩個要素分別是最優子結構和子問題重疊。
(1)最優子結構
如果乙個問題的最優解包含其子問題的最優解,我們就稱此問題具有最優子結構性質。因此,某個問題是否適合應用動態規劃演算法它是否具有最優子結構性質是乙個很好的線索(當然,具有最優子結構性質也可能意味著適合應用貪心策略)。 在動態規劃中,通常是自底向上地使用最優子結構。即首先求得子問題的最優解,然後在其中進行選擇,求原問題的最優解。在求解問題的過程中需在涉及的子問題中做出選擇,選擇出能得到原問題最優解的子問題。
(2)重疊子問題
在求解過程中如果遞迴演算法反覆求解相同的子問題,就稱最優化問題具有重疊子問題的性質。動態規劃演算法通常利用重疊子問題性質的做法是對每個子問題求解一次,將解存入乙個表中,當再次需要這個子問題時直接查表,每次查表的代表為常量時間。
3、有關術語-----無後效性
無後效性:我們要求狀態具有下面的性質:如果給定某一階段的狀態,則在這一階段以後過程的發展不受這階段以前各段狀態的影響,所有各階段都確定時,整個過程也就確定了。換句話說,過程的每一次實現可以用乙個狀態序列表示,在前面的例子中每階段的狀態是該線路的始點,確定了這些點的序列,整個線路也就完全確定。從某一階段以後的線路開始,當這段的始點給定時,不受以前線路(所通過的點)的影響。狀態的這個性質意味著過程的歷史只能通過當前的狀態去影響它的未來的發展,這個性質稱為無後效性。
二、個人理解
解決動態規劃問題的關鍵在於如何拆分問題,靠以下兩點:
1、狀態的定義
要解決乙個問題,首先要明白如何去定義該問題,(個人理解)求解某乙個狀態的,如最長非降序的子數列中,數列長度為n,求出長度i(i2、狀態轉移方程
狀態和狀態之間的關係式,就叫做狀態轉移方程。也就是怎麼根據已經求得的,得到未求得。每個階段的最優狀態可以從之前某個階段的某個或某些狀態直接得到,也就是找相應的規律。這個找規律的過程其實在狀態定義的過程中就應該了解,這裡需要用一定的dp方程給出。還有相應的邊界條件也得注意。
這裡引用知乎上王勐的回答中的一些動態規劃和別的方法的區別:
每個階段只有乙個狀態->遞推;三、例子1、如果我們有面值為1元、3元和5元的硬幣若干枚,如何能用最少的硬幣湊夠n元?每個階段的最優狀態都是由上乙個階段的最優狀態得到的->貪心;
每個階段的最優狀態是由之前所有階段的狀態的組合得到的->搜尋;
每個階段的最優狀態可以從之前某個階段的某個或某些狀態直接得到而不管之前這個狀態是如何得到的->動態規劃。
每個階段的最優狀態可以從之前某個階段的某個或某些狀態直接得到
這個性質叫做最優子結構;
而不管之前這個狀態是如何得到的
這個性質叫做無後效性。
(1)狀態初始化:我們要首先明白使用動態規劃時,陣列dp[ ]表示的意思。
下面我們分別給出n=0、1、2、3時的情況:
dp[ i ]表示,湊夠i 元,所需的最少硬幣數
(2)轉移方程:
可以看出,一般是,當前狀態可以由前乙個狀態加1得到,但是當可以用大硬幣的時候,我們還要比較時,我們還是要根據硬幣數的多少來更新當前狀態。
(3)**如下:
1 #include2 #include32、最長非降序子串行----------給定乙個陣列,求最大的非降子串行的長度。4using
namespace
std;56
intmain()7;
1415
for (int i = 1; i <= n; i++)
1623}24
25 cout << dp[n] <
26return0;
27 }
還是分兩步走:
(1)dp[ i ]表示陣列中array[0...i)中,最長的非降子串行的長度,並舉例給出相應的長度
(2)尋找前乙個或多個狀態和當前狀態的關係:
1 dp[i]=max; 其中,j**如下:值得注意的是非降序子串行的含義-----子串行中元素不一定得連續。如:,最長的長度是4,對應的序列是:1 #include2 #include3
4using
namespace
std;56
int lis(const vector &nums);78
intmain()9;
11 cout
12return0;
13}1415
int lis(const vector &nums)
1627}28
return dp[len-1
];29 }
知乎上王勐的回答
其實動態規劃中的最優狀態的說法容易產生誤導,以為只需要計算最優狀態就好,lis問題確實如此,轉移時只用到了每個階段「選」的狀態。但實際上有的問題往往需要對每個階段的所有狀態都算出乙個最優值,然後根據這些最優值再來找最優狀態。比如揹包問題就需要對前i個包(階段)容量為j時(狀態)計算出最大價值。然後在最後乙個階段中的所有狀態種找到最優值。
另外,還有很多有關動態規劃的題,可見我以前在leetcode分類中的解題過程。
ref:
動態規劃簡介
1.定義 動態規劃 dynamic programming 是運籌學的乙個分支,是求解決策過程 decision process 最優化的數學方法。20世紀50年代初美國數學r.e.bellman等人在研究多階段決策過程 multistep decision process 的優化問題時,提出了著名...
動態規劃基礎簡介
下邊就是自己的筆記 動態規劃演算法是將要解決的問題拆分成一系列相互交疊的子問題,通過推導關係定義子問題的求解策略,並隨時記錄子問題的解,最終獲得原始解,避免子相互交疊子問題的重複求解 這裡罵的是遞迴 動態規劃的三要素 中間結果儲存陣列 擅自加上的 最優子結構 每個階段最優狀態,可以從之前的某些狀態得...
動態規劃簡介及步驟
一 演算法介紹簡單來說,就是將乙個問題的多階段過程拆分為一系列單階段,逐步遞推,利用各階段間的關係,求出問題的最優解或解法之和。二 演算法思路 1.遞迴到動歸的一般轉化方法 遞迴函式有n個引數,就定義乙個n維的陣列,陣列下標是遞迴函式引數的取值範圍,陣列元素的指就是遞迴函式的返回值,這樣就可以從邊界...