動態規划的基本思想
動態規划的基本思想在於發現和定義問題中的子問題,這里子問題可也以叫做狀態;以及一個子問題到下一個子問題之間 是如何轉化的 也就是狀態轉移方程
因此我們遇到一個問題的時候 應該想一想這個問題是否能用某種方式表示成一個小問題,並且小問題具有最優子結構
最優子結構:問題的最優解由相關子問題的最優解組合而成,這些子問題可以獨立求解
關於最優子結構 我們來看2個示例
1、求無權有向圖中q-t的最短路徑
如果q-t間的最短路徑經過了點w 那么我們可以證明 q-w w-t也均是最短路徑
所以無權有向圖最短路徑是具有最優子結構的
2、求無權有向圖中q-t的最長的路徑

而無權有向圖最長路徑中
q-t的最長路徑是是q-r-t 但 q-r缺不是q-r的最長路徑 q-s-t-r是一條更長的路徑
所以無權有向圖最長路徑不具有最優子結構
2、關於動態規划的另一個要點便是思考稍小的子問題和下一個子問題間是如何轉化的也就是如何定義狀態轉移方程
狀態轉移方程的定義和我們是如何定義子問題的有關
比如:求最長連續回文串: 給出一個字符串S,求最長的連續回文串,例如串 babcbabcbaccba 最長回文是:abcbabcba
我們如果定義
p( i ) :以i結尾的最長回文串 我們會發現我們用子問題無法表示出p(i+1)
我們重新考慮一下原問題 最長連續回文串 如果用另一種方式來重新定義這個問題
已知字符串 S[0,n] 求回文傳S[i,j]中最長的那一個
我們可以定義以下子問題
P(i,j) : S[i,j] 是否是一個回文, P(i,i) = true
那么我們可以定義如下的狀態轉換方程
P(i,j) ={ (P(i+1,j-1) && (Si == Sj) }
總結起來我們可以用以下步驟去考慮一個問題如何用動態規划來解決
1、思考問題的最后一個步驟 是如何通過選擇構造得到最終答案的
2、根據構造情況來發現子問題
3、看看能否確定狀態轉移方程
動態規划與貪心等其他算法的比較
動態規划與分治,減治
分治 :將大問題分成若干個小問題去解決 遞歸的求解每個小問題,每個小問題之間沒有關系 例如 快排
減治 :將大問題縮減成小問題,減掉的部分不需要考慮,例如:二分查找
動態規划:將原問題分成多個子問題,不同子問題間存在一定的聯系,相互間有重疊的子問題
這里我個人認為動態規划分治 減治都是將大問題拆分成小問題 進行求解 區別在於
減治法減掉的部分 可以不用再求解了;
分治法每個小問題都需要進行求解;
動態規划不同的子問題間是有相互重疊的子問題的
動態規划與貪心
動態規划在於我們求解了所有子問題 雖然有些子問題最終並不能組成答案
而貪心算法任務無需求解所有子問題,所以選擇在當前情況下最優的情況自頂向下的求解問題,貪心可以認為是動態規划的一個特例
如果用一個樹來表示子問題的話 可以認為動態規划考慮了樹中的所有節點
而貪心算法減去了樹了許多枝干,在考慮了通向最優解的那一條路
常見的可以用動態規划解決的問題
1、最大連續子序列和:
給定k個整數的序列{N1,N2,...,Nk },其任意連續子序列可表示為{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= k。最大連續子序列是所有連續子序中元素和最大的一個,
例如給定序列{ -2, 11, -4, 13, -5, -2 },其最大連續子序列為{11,-4,13},最大連續子序列和即為20。
2、最大連續子序列乘積:
給一個浮點數序列,取最大乘積連續子串的值,例如 -2.5,4,0,3,0.5,8,-1,則取出的最大乘積連續子串為3,0.5,8。也就是說,上述數組中,3 0.5 8這3個數的乘積30.58=12是最大的,而且是連續的。
3、求最長連續回文串:
給出一個字符串S,求最長的連續回文串,例如串 babcbabcbaccba 最長回文是:abcbabcba
4、字符串相似度:
把兩個字符串變成相同的基本操作定義如下:
1. 修改一個字符(如把 a 變成 b)
2. 增加一個字符 (如 abed 變成 abedd)
3. 刪除一個字符(如 jackbllog 變成 jackblog)
針對於 jackbllog到jackblog 只需要刪除一個或增加一個 l 就可以把兩個字符串變為相同。把這種操作需要的次數定義為兩個字符串的距離 L, 則相似度定義為1/(L+1) 即距離加一的倒數。那么jackbllog和jackblog的相似度為 1/1+1=1/2=0.5 也就是所兩個字符串的相似度是 0.5。
給定任意兩個字符串,你是否寫出一個是否來計算出它們的相識度。
5、最長公共子序列
對於序列S和T,求它們的最長公共子序列。例如X={A,B,C,B,D,A,B},Y={B,D,C,A,B,A}則它們的lcs是{B,C,B,A}和{B,D,A,B}。求出一個即可。
針對最大連續子序列乘積給出一段講解與代碼
最大連續子序列和:
給定k個整數的序列{N1,N2,...,Nk },其任意連續子序列可表示為{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= k。最大連續子序列是所有連續子序中元素和最大的一個,
例如給定序列【-2, 11, -4, 13, -5, -2】,其最大連續子序列為{11,-4,13},最大連續子序列和即為20。
思路:
20這個答案 是我們比較了【-2】,【11】,【-4】,【-2,11,-4,11】,【11,-4,13】,【11,-4,13,-5】,【11,-4,13,-5。-2】這幾個序列之后得到
所以我們定義子問題是
Max(i) : 以i結尾的連續序列的最大和
狀態轉移方程是:
Max[i] = max{a[i], Max[i-1] + a[i])
代碼如下:
int maxSubArray(int A[], int n) {
int ans=A[0],i,j,sum=0;
for(i=0;i<n;i++){
sum+=A[i];
ans=max(sum,ans);
sum=max(sum,0);
}
return ans;
}
