動態規劃與分治方法類似,都是通過組合子問題的解來求解原問題.需要注意的是,動態規劃(dynamic programming)這裡的programming
並不是指編寫電腦程式,而是指一種**法.
分治方法將問題劃分為互不相交的子問題,遞迴地求解子問題,再將它們的解組合起來,求出原問題的解.
動態規劃也是通過組合子問題的解來求解原問題,但是與分治方法不同的是,動態規劃應用於子問題重疊的情況.換句話說,就是不同的問題具有公共的子問題,這種情況下,分治方法會做許多不必要的工作,它會反覆地求解那些公共子問題.而動態規劃對每個子問題只求解一次,將其解儲存在**
中,從而避免了不必要的工作.
acm時期最害怕的就是這類題,但是想到狀態轉移方程就不難,**量也短.
現在從原理上從零開始.
最長公共子串行簡稱lcs
注意子串行是不連續的.像下面這個例子,設x與y的最長公共子串行為lcs(x,y)
.
\[\begin x:abcbdab \\y:bdcaba\end
\]通過肉眼觀察,我們可以發現子串行有如:a,ab,bcb等,其中最長的是bdab,bcab,bcba,長度為\(4\)
通過分析,如果用暴力列舉
的方法,思路就是x有的子串行y也有.
總的來說就是列舉x的所有子串行,然後在y中check
來分析一下這個複雜度:
總複雜度是指數級別的,所以這個演算法是龜速的.
現在必須設計乙個更給力的演算法
做乙個簡化:
為了方便,定義\(|s|\)為字串\(s\)的長度
如果只是求長度,就不需要考慮所有的子串行,只用考慮他們的字首.
定義\(c[i,j]=|lcs(x[1..i],y[1...j])|\),那麼\(c[m,n]\)就是最終答案
初始化\(c[0,0]=0\)
|lcs(x,y)|
的狀態轉移方程是:
\[c[i,j]=\begin0&\text \\c[i-1,j-1]+1&\text \\
max(c[i,j-1],c[i-1,j])&\text
\end
\]我們來感性證明一下:
令\(z[1...k]=lcs(x[1...i],y[1...j])\)當\(c[i,j]=k\)的時候
同時若\(z[k]\)的取值也為\(x[i]\),等於\(y[j]\),那就是說\(z[1...k-1]=lcs(x[1...i-1],y[1...j-1])\),而\(c[i-1,j-1]=k-1\)
反證法假設存在\(c[i-1,j-1]>k-1\),因為\(x[i]=y[j]\),所以\(c[i,j]=c[i-1,j-1]+1\),意味著\(c[i,j]>k\),與原命題矛盾,假設不成立.得證
其他情況也差不多.
比如假設\(z[k]=x[i]\neq y[j]\),就是說\(z[1...k-1]=lcs(x[1...i-1],y[j])\),而\(c[i-1,j]=k\),之後證明也簡單
如果問題的最優解所包含的子問題的解也是最優的,我們就稱該問題具有最優子結構性質(即滿足最優化原理)。最優子結構性質為動態規劃演算法解決問題提供了重要線索。
如果\(z=lcs(x,y)\),那麼任何\(z\)的字首都是某個\(x\)的字首和某個\(y\)的字首的\(lcs\)
問題重疊性質是指在用遞迴演算法自頂向下對問題進行求解時,每次產生的子問題並不總是新問題,有些子問題會被重複計算多次。動態規劃演算法正是利用了這種子問題的重疊性質,對每乙個子問題只計算一次,然後將其計算結果儲存在乙個**中,當再次需要計算已經計算過的子問題時,只是在**中簡單地檢視一下結果,從而獲得較高的效率。
對於\(lcs\)的**而言,複雜度最大的那一定是失配的時候,要計算兩個,實際上不需要重複計算,可以像填表一樣先記下來.
將各階段按照一定的次序排列好之後,對於某個給定的階段狀態,它以前各階段的狀態無法直接影響它未來的決策,而只能通過當前的這個狀態。換句話說,每個狀態都是過去歷史的乙個完整總結。這就是無後向性,又稱為無後效性。
求出長度的過程中記錄路徑,最後再通過回溯法就可以得出\(lcs\)
總時間複雜度是\(o(n*m)\),空間複雜度是\(o(n*m)\)
但是求長度的空間複雜度可以降為\(o(min(n,m))\)
可以發現,每個元素的計算只與其左上,左邊,上邊這三個因素有關.
/**
* 最長公共子串行
* 優化了記憶體空間的使用
* 觀察到一件事: 每乙個元素的計算,只和其在左上, 左邊, 上邊的三個元素相關
* 可以考慮len(x) + 3
* 3個變數 定義為leftabove, left, above
*/#include#include#includeusing namespace std;
int lcs(string x, string y);
int main()
int lcs(string x, string y)
} cout但是如果空間複雜度為\(o(min(n,m))\),那還能得出路徑嗎?
演算法導論 動態規劃
動態規劃這個演算法,我一直都搞不明白,也許因為我數學能力太差的緣故,總是不得其要領,每次學習這個演算法的時候,總是不知道所謂的狀態轉移方程到底是怎麼樣推導出來的。其實就在我寫這篇部落格的時候,我依然不清楚。什麼問題能用動態規劃來解決呢?動態規劃問題的特徵就是最優子結構,乙個遞迴結構 該問題需要求乙個...
動態規劃 鋼條切割《演算法導論》
給定各長度鋼鐵單位 以及乙個長度為n的鋼條,求最大效益 1 10 分別為p include includeint memorized cut rod aux int p,int len,int r 樸素遞迴演算法 演算法複雜度為2 n級這裡寫 int cut rod int p,int len in...
演算法導論 動態規劃 鋼條切割
這幾天一直在看動態規劃的問題,看了看也沒想象中那麼難,但需要好好的分析一下問題,我們就拿演算法導論的鋼條切割問題好好理解一下,鋼條切割問題就是給你一段鋼條,讓你把鋼條切割成好多份,因為每一段鋼條都有不同的價值,我們要找到最優的切割方案,使切割成的鋼條價值最大。這道題我們可以用dfs直接搜尋,對每個狀...