動態規划——遞歸寫法和遞推寫法


一、什么是動態規划

  動態規划(DP)是一種用來解決一類最優化問題的算法思想。簡單來說,動態規划將一個復雜的問題分解成若干個子問題,通過綜合子問題的最優解來得到原問題的最優解。

 

 

二、動態規划的遞歸寫法

  以斐波那契(Fibonacci) 數列為例,斐波那契數列的定義為 F0=1,F1=1,Fn=Fn-1+Fn-2 (n≥2)。為了避免重復計算,可以開一個一維數組 dp,用以保存已經計算過的結果。代碼如下:

 1 int dp[maxn];
 2 // 斐波那契數列遞歸寫法
 3 int F(int n) {
 4     if(n == 0 || n==1)    return 1;    // 遞歸邊界
 5     if(dp[n] != -1)    return dp[n];    // 已經計算過 
 6     else {
 7         dp[n] = F(n-1) + F(n-2);    // 計算F(n),並保存 
 8         return dp[n];                // 返回 F(n) 結果 
 9     } 
10 } 

 

 

 

三、 動態規划的遞歸寫法

  以經典的數塔問題為例,如下圖所示,將一些數字排成數塔的形狀,其中第一層有一個數字,第二層有兩個數字……第 n 層有 n 個數字。現在要從第一層走到第 n 層,每次只能走向下一層連接的兩個數字中的一個,問:最后將路徑上所有數字相加后得到的和最大是多少?

  

  不妨令 dp[i][j] 表示從第 i 行第 j 個數字出發到達最底層的所有路徑中能得到的最大和,在定義了這個數組后,dp[1][1] 就是最終想要的答案。

  如果想求出 dp[i][j],那么一定要先求出它的兩個子問題“從位置 (i+1,j) 到達最底層的最大和 dp[i+1][j]”和“從位置 (i+1,j+1) 到達最底層的最大和 dp[i+1][j+1]”,即進行了一次決策:走左下還是右下。寫成式子就是:

              dp[i][j] = max(dp[i+1][j], dp[i+1][j+1]) + f[i][j]

  其中 f[i][j] 存放第 i 層的第 j 個數字。代碼如下:

 1 /*
 2     動態規划的遞推寫法 
 3 */
 4 
 5 #include <stdio.h>
 6 #include <string.h>
 7 #include <math.h>
 8 #include <stdlib.h>
 9 #include <time.h>
10 #include <stdbool.h>
11 
12 #define maxn 1000
13 int f[maxn][maxn], dp[maxn][maxn]; 
14 
15 // 較大值 
16 int max(int a, int b) {
17     return a>b ? a : b;
18 }
19 
20 int main() {
21     int n;
22     scanf("%d", &n);
23     int i, j;
24     for(i=1; i<=n; ++i) {
25         for(j=1; j<=i; ++j) {
26             scanf("%d", &f[i][j]);        // 輸入數塔 
27         }
28     }
29     // 邊界
30     for(j=1; j<=n; ++j) {
31         dp[n][j] = f[n][j];
32     } 
33     // 從第 n-1 層往上計算 dp[i][j]
34     for(i=n-1; i>=1; --i) {
35         for(j=1; j<=i; ++j) {
36             dp[i][j] = max(dp[i+1][j], dp[i+1][j+1]) + f[i][j]; 
37         }
38     } 
39     printf("%d\n", dp[1][1]);            // dp[1][1] 即為需要的答案 
40 
41     return 0;
42 }

   

 

  下面指出兩對概念的區別:

  1.  分治與動態規划。分治和動態規划都是將問題分解為子問題,然后合並子問題的解得到原問題的解。但是不同的是,分治法分解出的子問題是不重疊的,因此分治法解決的問題不擁有重疊子問題,而動態規划解決的問題擁有重疊子問題。另外,分治法解決的問題不一定是最優化問題,而動態規划解決的問題一定是最優化問題。

  2.  貪心與動態規划。貪心和動態規划都要求原問題必須擁有最優子結構。二者的區別在於,貪心法通過一種策略直接選擇一個子問題去求解,沒被選擇的子問題就不去求解了,直接拋棄。也就是說,它總是只在上一步選擇的基礎上繼續選擇,因此整個過程以一種單鏈的流水方式進行。而動態規划總是從邊界開始向上得到目標問題的解。也就是說,它總是會考慮所有子問題,並選擇繼承能得到最優結果的那個,對暫時沒被繼承的子問題,由於重疊子問題的存在,后期可能會再次考慮它們,因此還有機會成為全局最優的一部分,不需要放棄。    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM