動態規劃詳解

2021-10-01 08:32:46 字數 2364 閱讀 9093

從一道題目出發:求兩個字串的最大公共子串長度,比如字串str1:bdcaba和字串str2:abcbdab的最大公共子串即為紅色部分bcba,長度為4

如何從動態規劃的思想出發解題呢,動態規劃的特點是當前最優解是從之前的子最優解和當前狀態分析得到,是一種遞推的過程,比如上述最大公共子串,對於長度為n的字串1和長度為m的字串2的最大公共子串長度記為f(n,m),這個是動態規劃的核心,只有正確表示出了題目要求的公式才能進行下一步分析,因此,對於n-1的字串1和m-1的字串2的最大公共子串長度為f(n-1,m-1)。當求長度為n的字串1和長度為m的字串2的最大公共子串長度時,只需比較str1[n]與str2[m],顯然如果str1[n] == str2[m],f(n,m)=f(n-1,m-1)+1,即已解出子最優解加1;當str1[n] != str2[m]時,由於現在要求求f(n,m),所以f(n-1,m)和f(n,m-1)已經求過了,既然str1[n]和str2[m]不相等,則必是f(n-1,m)和f(n,m-1)二者最大的那個。

通過以上分析,便可得到如下公式:

**如下

#include using namespace std;

int max_public_str1(const string& str1, const string& str2)

else

}} return p[str1.length()][str2.length()];

}

其中,f(n,m)用乙個n*m的二維陣列表示,記錄每個位置str1與str2的最大公共子串長度,初始值是0,其實,嚴格意義上,只有f(0,1...m)和f(1...n,0)為0,表示字串1長度為0,無論字串2長度是幾,公共子串長度都是0,同理字串2長度為0的情況,但是從**的角度,定義的二維陣列變數p[n][m]如果只初始化p[0][0...m]和p[0...n][0],即只初始化第0行和第0列的資料,不符合**規範,因為有未定義的變數值,另外即使陣列都初始化為0,也是合理的,因為此時還沒有開始計算。

對於使用二維陣列記錄子最優解結果的方式,顯然其空間複雜度為o(n*m),那麼能否減少空間複雜度呢,答案是肯定的。通過上述f(n,m)的計算公式,可以看出,無論str1[n]與str2[m]是否相等,f(n,m)的公式中只出現了f(n-1,m-1),f(n,m-1),f(n-1,m)這三個已經求出的值,因此,可以通過乙個2*m的二維陣列p記錄1到m長度時,與1到n的長度最大公共子串長度,這裡二維陣列p是相當於兩個長度為m的一維陣列,乙個表示前一行乙個表示當前行,並非第一行就是前一行,第二行就是當前行,而是隨著對str1字串的遍歷交替更新的,這就是滾動陣列意思。**如下:

#includeusing namespace std;

int max_public_str2(const string& str1, const string& str2)

else

}} return p[str1.length()%2][str2.length()];

}

通過上述**可以看出原來n*m的陣列p改為了2*m的陣列,一維座標通過i%2在0和1之間進行更替。通過f(n,m)的公式知道,對於n*m的陣列p記錄結果時,在計算p[i][j]時只用到了p[i-1][j-1],p[i-1][j],p[i][j-1],顯然只用到了第i-1行和第i行,而對於0到i-2行的資料已經 不需要了。因此,就可以只用兩個長度為m的一維陣列或是2*m的二維陣列記錄這兩行的結果。比如計算i=1時p[(1-1)%2=0][...](第一行)是上一次的資料,p[1%2=1][...](第二行)是要計算的資料,當i=2時,p[1%2=1][...]就是上一次的資料,原本應該將本次結果記錄在p[2][...](第三行)中,但是只用兩行資料空間,而p[2%2=0][...](第一行)的資料已經沒有用了,所以可以直接將本次計算的資料儲存在p[2%2=0][...]中,當i=3時,p[(3-1)%2=0][...]是上一次的資料,p[3%2=1][...]中資料已經沒有用了,所以直接將計算結果儲存在p[3%2=1][...]中,依次類推,當計算到i時,p[(i-1)%2][...]是上一次資料,p[i%2][...]是上上一次資料,本次計算用不到了,所以直接在p[i%2][...]記錄本次結果。

小結:顯然直接根據題意建立對應陣列記錄結果比較容易理解,比如上述題目用二維陣列直接表達公式很容易理解,但是使用滾動陣列在理解上會有些困難。其實,滾動陣列就是直接使用陣列中的值,因此只要確保用使用的已有資料在使用前沒有改變。

還有一類動態規劃其記錄結果的陣列不適於更改為滾動陣列。

有題目出發,比如有如下題目:

有n堆石子排成一排,每堆石子有一定的數量。現要將n堆石子並成為一堆。合併的過程只能每次將相鄰的兩堆石子堆成一堆,每次合併花費的代價為這兩堆石子的和,經過n-1次合併後成為一堆。求出總的代價最小值(獲最大代價)。

假設通常其公式如下(其中min如果是max,就是求最大值)

動態規劃詳解

其實根本就談不上詳解,應該說只是隨便談談,真正能詳解動態規劃的又有幾個人,所以,這個標題略顯扯淡。前段時間一直在做關於資料結構的題,也算是對資料結構有了一定的了解,知道了有些資料結構的基本演算法。現在剛剛開始接觸動態規劃,其實寫這篇文章的初衷是一來鍛鍊一下自己的總結能力,二來也是希望通過這篇文章,來...

《動態規劃》 ACM 動態規劃例題詳解

描述 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 圖1 圖1給出了乙個數字三角形。從三角形的頂部到底部有很多條不同的路徑。對於每條路徑,把路徑上面的數加起來可以得到乙個和,你的任務就是找到最大的和。注意 路徑上的每一步只能從乙個數走到下一層上和它最近的左邊的那個數或者右邊的那個數。輸...

動態規劃詳解(2)

接下來,讓我們來看看如何解決二維的dp問題。問題描述 平面上有n m個格仔,每個格仔中放著一定數量的蘋果。你從左上角的格仔開始,每一步只能向下走或是向右走,每次走到乙個格仔上就把格仔裡的蘋果收集起來,這樣下去,你最多能收集到多少個蘋果。解這個問題與解其它的dp問題幾乎沒有什麼兩樣。第一步找到問題的 ...