動態規劃演算法
2023年03月28日
[b]動態規劃演算法 [/b]
[b]作者 july 二零一零年十二月三十一日 [/b]
本文參考:微軟面試100題系列v0.1版第19、56題、演算法導論、維基百科。
ok,咱們先來了解下什麼是動態規劃演算法。
動態規劃一般也只能應用於有最優子結構的問題。最優子結構的意思是區域性最優解能決定全域性最優解
(對有些問題這個要求並不能完全滿足,故有時需要引入一定的近似)。
簡單地說,問題能夠分解成子問題來解決。
動態規劃演算法分以下4個步驟:
1.描述最優解的結構
2.遞迴定義最優解的值
3.按自底向上的方式計算最優解的值 //此3步構成動態規劃解的基礎。
4.由計算出的結果構造乙個最優解。 //此步如果只要求計算最優解的值時,可省略。
好,接下來,咱們討論適合採用動態規劃方法的最優化問題的倆個要素:
最優子結構性質,和子問題重疊性質。
一、最優子結構。
如果問題的最優解所包含的子問題的解也是最優的,我們就稱該問題具有最優子結構性質(即滿足最優化原理)。
意思就是,總問題包含很多歌子問題,而這些子問題的解也是最優的。
二、重疊子問題。
子問題重疊性質是指在用遞迴演算法自頂向下對問題進行求解時,每次產生的子問題並不總是新問題,
有些子問題會被重複計算多次。動態規劃演算法正是利用了這種子問題的重疊性質,對每乙個子問題只計算一次,
然後將其計算結果儲存在乙個**中,當再次需要計算已經計算過的子問題時,只是在**中簡單地檢視一下結果,
從而獲得較高的效率。
ok,咱們馬上進入面試題第56題的求解,即運用經典的動態規劃演算法:
56.最長公共子串行。
題目:如果字串一的所有字元按其在字串中的順序出現在另外乙個字串二中,
則字串一稱之為字串二的子串。
注意,並不要求子串(字串一)的字元必須連續出現在字串二中。
請編寫乙個函式,輸入兩個字串,求它們的最長公共子串,並列印出最長公共子串。
例如:輸入兩個字串bdcaba和abcbdab,字串bcba和bdab都是是它們的最長公共子串,
則輸出它們的長度4,並列印任意乙個子串。
分析:求最長公共子串(longest common subsequence, lcs)是一道非常經典的動態規劃題,
因此一些重視演算法的公司像microstrategy都把它當作面試題。
步驟一、描述乙個最長公共子串行
先介紹lcs問題的性質:記xm=和yn=為兩個字串,
並設zk=是x和y的任意乙個lcs,則可得出3條性質:
1. 如果xm-1=yn-1,那麼zk-1=xm-1=yn-1,並且zk-1是xm-1和yn-1的乙個lcs;
2. 如果xm-1≠yn-1,那麼當zk-1≠xm-1時,z是xm-1和y的lcs;
3. 如果xm-1≠yn-1,那麼當zk-1≠yn-1時,z是x和yn-1的lcs;
下面簡單證明一下由上述相應條件得出的這些性質:
1. 如果zk-1≠xm-1,那麼我們可以把xm-1(yn-1)加到z中得到z',這樣就得到x和y的乙個長度為k+1的公共子串z'。
這就與長度為k的z是x和y的lcs相矛盾了。因此一定有zk-1=xm-1=yn-1。
既然zk-1=xm-1=yn-1,那如果我們刪除zk-1(xm-1、yn-1)得到的zk-1,xm-1和yn-1,顯然zk-1是xm-1和yn-1的乙個公共子串,現在我們證明zk-1是xm-1和yn-1的lcs。用反證法不難證明。假設有xm-1和yn-1有乙個長度超過k-1的公共子串w,那麼我們把加到w中得到w',那w'就是x和y的公共子串,並且長度超過k,這就和已知條件相矛盾了。
2. 還是用反證法證明。假設z不是xm-1和y的lcs,則存在乙個長度超過k的w是xm-1和y的lcs,那w肯定也x和y的公共子串,而已知條件中x和y的公共子串的最大長度為k。矛盾。
3. 證明同2。
步驟二、乙個遞迴解
根據上面的性質,我們可以得出如下的思路:
求兩字串xm=和yn=的lcs,
如果xm-1=yn-1,那麼只需求得xm-1和yn-1的lcs,並在其後新增xm-1(yn-1)即可(上述性質1);
如果xm-1≠yn-1,我們分別求得xm-1和y的lcs和yn-1和x的lcs,並且這兩個lcs中較長的乙個為x和y的lcs(上述性質2、3)。
根據上述結論,可得到以下公式,
如果我們記字串xi和yj的lcs的長度為c[i,j],我們可以遞迴地求c[i,j]:
/ 0 if i=0 and xi=xj
\ max(c[i,j-1],c[i-1,j] if i,j>=0 and xi≠xj
上面的公式用遞迴函式不難求得。自然想到fibonacci第n項(本微軟等100題系列v0.1版第19題)問題的求解中可知,
直接遞迴會有很多重複計算,所以,我們用從底向上迴圈求解的思路效率更高。
為了能夠採用迴圈求解的思路,我們用乙個矩陣(參考下文文末**中的lcs_length)儲存下來當前已經計算好了的c[i,j],
當後面的計算需要這些資料時就可以直接從矩陣讀取。
另外,求取c[i,j]可以從c[i-1,j-1] 、c[i,j-1]或者c[i-1,j]三個方向計算得到,
相當於在矩陣lcs_length中是從c[i-1,j-1],c[i,j-1]或者c[i-1,j]的某乙個各自移動到c[i,j],
因此在矩陣中有三種不同的移動方向:向左、向上和向左上方,其中只有向左上方移動時才表明找到lcs中的乙個字元。
於是我們需要用另外乙個矩陣(參考下文文末**中的lcs_direction)儲存移動的方向。 步驟三,計算lcs的長度
lcs-length(x, y)
1 m ← length[x]
2 n ← length[y]
3 for i ← 1 to m
4 do c[i, 0] ← 0
5 for j ← 0 to n
6 do c[0, j] ← 0
7 for i ← 1 to m
8 do for j ← 1 to n
9 do if xi = yj
10 then c[i, j] ← c[i - 1, j - 1] + 1
11 b[i, j] ← "
動態規劃演算法
一 動態規劃演算法原理 將待求解的問題分解成若干個相互聯絡的子問題,先求解子問題,然後從這些子問題的解得到原問題的解 對於重複出現的子問題,只在第一次遇到的時候對它進行求解,並把答案儲存起來。了不去求解相同的子問題,引入乙個陣列,把所有子問題的解存於該陣列中,這就是動態規劃所採用的基本方法。動態規劃...
動態規劃演算法
動態規劃 通過把原問題分解為相對簡單的子問題來求解複雜問題。動態規劃常常適用於有重疊子問題和最優子結構性質的問題。演算法總體思想 演算法的基本步驟 演算法的基本要素 最優子結構 重疊子問題 備忘錄方法 問題描述 子串行 公共子串行 最長公共子串行 lcs 問題 問題分析 動態規劃求解lcs問題 最長...
動態規劃演算法
動態規劃演算法的思路 動態規劃法即 dynamic programming method dp 是系統分析中的種常用方法。動態規劃法是20世紀50年代由貝爾曼 r.bellman 等人提出的,用來解決多階段決策過程問題的一種最優化方法。多階段決策過程是指把研究問題分成若干個相互聯絡的階段,由每個階段...