C#筆記:動態規劃演算法

  • 2019 年 11 月 22 日
  • 筆記

一個複雜的問題,通常逐級會分解成一系列小問題。而且很多情況下,某些小問題是相似的。但是使用傳統的遞歸,會將相同相似的小問題進行重複的計算。這樣浪費了算力。 所謂動態規劃演算法,實質是使用一個表,記錄下所有小問題的值。如果在計算中再次遇到相同問題,則直接取值。不做計算。顯然,如果每個小問題都是不同的,那麼此演算法沒有優勢。 比如斐波那契數列,就是一個簡單的例子。 定義:

Fab(n)= Fab(n-1)+Fab(n-2)  Fab(1)=Fab(2)=1;

實現1:

static int GetFab(int n)          {              if (n == 1) return 1;              if (n == 2) return 1;              return GetFab(n - 1) + GetFab(n - 2);          }

假如我們求Fab(5) 。那我們需要求Fab(4) +Fab(3)。 Fab(4)=Fab(3)+Fab(2)…..顯然。 Fab(3)被電腦不加區別的計算了兩次。而且隨著數字的增大,計算量是指數增長的。 如果我們使用一個數組,記錄下Fab的值。當Fab(n)!=null 時。直接讀取。那麼,我們就能把時間複雜度控制在 n 以內。 實現2:

static int[] fab = new int[6];  static int GetFabDynamic(int n)          {              if (n == 1) return fab[1] = 1;              if (n == 2) return fab[2] = 1;              if (fab[n] != 0)//如果存在,就直接返回。              {                  return fab[n];              }              else //如果不存在,就進入遞歸,並且記錄下求得的值。              {                  return fab[n] = GetFabDynamic(n - 1) + GetFabDynamic(n - 2);              }          }

這就是,動態規劃演算法的 備忘錄模式。只需要把原來的遞歸稍微修改就行了。 下面是0-1背包問題的解法。可以對比一下。(一個限重w的背包,有許多件物品。sizes[n]保存他們的重量。values[n]保存它們的價值。求不超重的情況下背包能裝的最大價值) 

static int[] size = new int[] { 3, 4, 7, 8, 9 };// 5件物品每件大小分別為3, 4, 7, 8, 9 且是不可分割的  0-1 背包問題          static int[] values = new int[] { 4, 5, 10, 11, 13 };//// 5件物品每件的價值分別為4, 5, 10, 11, 13          static int capacity = 16;          static int[,] dp = new int[6, capacity + 1];          static int knapsack(int n, int w)          {              if (w < 0) return -10000;              if (n == 0) return 0;              if (dp[n, w] != 0)              {                  return dp[n, w];              }              else              {                  return dp[n, w] = Math.Max(knapsack(n - 1, w), knapsack(n - 1, w - size[n - 1]) + values[n - 1]);                    /*                                    * knapsack(n,w) 指在前N件物品在W剩餘容量下的最大價值。                   * 這個公式的意思是,對於某一件物品,                   * 1、如果選擇裝進去,那麼,當前價值=前面的n-1件物品在空位w - size(n)下的最大價值(因為空位需要留出,空位也隱含了價值)。                   * 再加上這件物品的價值。等於 knapsack(n - 1, w - size[n - 1]) + values[n - 1]                   * 2、 如果我們選擇不裝進去,那麼,在n-1物品的情況下空位仍然在,當前價值 = 前面n-1件物品在空位w下的最大價值。                   * 等於knapsack(n - 1, w)                   * 注意:隨著演算,某一情況下的價值不會一成不變。                   * 此時我們做出決策:到底是在裝入時的價值大,還是不裝入時的價值大呢?我們選擇上面兩種情況中值較大的。並記錄在案。                   * 最後dp[N,M]就是我們要求的值。                   */            }          }