動態規劃演算法 DP 學習 1

2021-10-11 14:30:46 字數 3168 閱讀 6820

是一種用來最優化問題的方法,將乙個複雜問題分解成若干個子問題,再通過將每個子問題的最優解記錄下來,這樣在再次碰到子問題的時候,就有乙個直接的答案。重點就是其取消了重複計算環節。

通常使用遞迴遞推兩種方法實現動態規劃,遞迴又叫做:記憶化搜尋

可以從乙個fibonacci數列的計算為例:

計算斐波那契數列的經典方法就是遞迴,也就是如下:

intf(

int n)

這裡就涉及了重複計算的問題,例如我要計算f(5)。那麼要計算f(4)和f(3)而f4=f3+f2,f3=f2+f1;可以看到f2被計算了兩遍。如果可以把重複計算的值在計算一遍後儲存起來,則可以實現程式的優化。

分析其迭代過程:可以用乙個一位陣列dp[maxn]記錄被計算過的f(n)的值。

dp[i]=f(i-1)+f(i-2);
那麼可以將原函式改寫:

intf(

int n)

這裡就可以看出記憶化搜尋的意思來,就是用乙個陣列記錄需要被重複計算的部分,等到再次呼叫到的時候直接輸出。

記憶化搜尋能夠把複雜度降級。

這裡可以引出動態規劃的乙個要素,就是重疊子問題最優子結構,它被分解為幾個子結構後,每個子結構每次決策過程中要有最優解。

下面以乙個問題為例,再分步思考動態規劃問題。

給定乙個數字序列a1,a2,a3...,an,求i,j(1<=i<=j<=n),使得ai+...+aj最大,輸出最大和
這裡使用動態規劃解法,假設乙個dp[i]一維陣列。

他的子問題是什麼?假設現在有

a1=-2, a2=11, a3=-4, a4=-3
暴力方法就是遍歷,其過程可以寫下為:

a[1->1]=-2  a[1->2]=-2+11  a[1->3]=-2+11-4  a[1->4]=-2+11-4-3

a[2->2]=11 a[2->3]=11-4 a[2->4]=11-4-3

a[3->3]=-4 a[3->4]=-4-3

a[4->4]=-3

總結可以得到其子問題:

在這裡需要考慮:需要得到的結果是最大值。可以將最大值設為dp[i],表示以a[i]為結尾的一系列最大值。

而每次計算得到的最大值是什麼?

dp[i]=dp[i-1]+a[i]
這裡再考慮如果a[i]+dp[i-1]而邊界條件根據i-1>=0,則i>=1,所以需要單獨考慮i=0的情況:

dp[0]=a[0]
這樣就不會發生在迭代的時候卡殼的問題。

整個邏輯完成了之後就可以開始**編寫了。

#include

#include

using

namespace std;

const

int maxn=

10010

;int a[maxn]

,dp[maxn]

;int

main()

//數列輸入完畢

dp[0]

=a[0];

for(

int i=

1;i)//已經完成,接下來比較輸出

int k=0;

for(

int i=

1;i)printf

(%d,

&dp[k]);

return dp[k]

;}

再來看乙個問題

給出乙個字串s,求s的最長回文子串長度

eg:字串"patzjujztaccbcc"的最長回文子串為"atzjujzta",長度為9。

怎麼判斷是不是回文串?這是首要的問題。

傳統判斷回文的方法:

兩頭夾:先判斷s[0] 與s[len-1],再是s[1]與s[len-1-1],。。。。以此迴圈
那麼判斷是不是回文的方法就確定了,這是乙個可以分解成子問題的過程。

暴力方法就是遍歷所有的子串組合,在這個過程中,有許多的重複計算。

在每次判定的組合裡,都會重複之前的步驟,判定某個子串是不是回文。

那麼是否需要乙個數列,儲存之前計算過的關於某子串是不是回文的資訊?

這就是優化的地方。

那麼判斷乙個子串是不是回文的dp寫法呢?

//偽**

bool dp[i][j];

if(s[i]==s[j])

dp[i][j]==dp[i+1][j-1]; //j>=1

else if(s[i]!=s[j])

dp[i][j]=0;

我判斷我自身是不是回文,只需要先比較我的兩頭是不是相等,再判斷我裡面是不是回文,而裡面是不是回文這個資訊已經在之前計算過了。

這是乙個遞迴的過程。

那麼其邊界條件需要考慮的:

1.自身與自身,長度為1的回文子串,不能讓不能讓i+1>j-1

2.根據i+1>j-1,則j>i+2;那麼j=i+1的情況呢?要設定為邊界條件。

//兩條都是根據j>i+2的條件來寫的

for(i=0;i完成了設定後就可以開始寫**了。

#include

#include

#define maxn 1010

using

namespace std;

intmain()

}//這裡進行篩選的方式也需要考慮,是根據i,j雙迴圈的方式進行?還是根據固定長度l,遍歷完乙個長度的序列後再遍歷l++的值呢

//看哪種方式能夠不卡殼,那麼i,j雙迴圈的方式明顯不行,選用固定長度方式

for(

int l=

3;lsize()

;l++)if

(dp[i]

[j]==1)

ans=j-i+1;

}return ans;

}

《演算法筆記》

動態規劃演算法(DP)

動態規劃演算法採用分治演算法的思想,將原問題分成若干個子問題,然後分別求解各個子問題,最後將子問題的解組合起來得到原問題的解。分治演算法遞迴地求解各個子問題,可能重複求解某些子問題。與分治演算法不同的是,動態規劃演算法不是遞迴地求解各個子問題,它是從簡單問題的解入手,逐步求解,直至求解出原問題。動態...

動態規劃演算法

一 動態規劃演算法原理 將待求解的問題分解成若干個相互聯絡的子問題,先求解子問題,然後從這些子問題的解得到原問題的解 對於重複出現的子問題,只在第一次遇到的時候對它進行求解,並把答案儲存起來。了不去求解相同的子問題,引入乙個陣列,把所有子問題的解存於該陣列中,這就是動態規劃所採用的基本方法。動態規劃...

動態規劃演算法

動態規劃 通過把原問題分解為相對簡單的子問題來求解複雜問題。動態規劃常常適用於有重疊子問題和最優子結構性質的問題。演算法總體思想 演算法的基本步驟 演算法的基本要素 最優子結構 重疊子問題 備忘錄方法 問題描述 子串行 公共子串行 最長公共子串行 lcs 問題 問題分析 動態規劃求解lcs問題 最長...