動態規劃思想及經典算法

2021-02-28 LOA算法學習筆記

對於給定一個問題,可以將其分解成不同子問題,之後對對其不同子問題 進行求解,再合併子問題解以得出原問題的解。

通常情況下,子問題具有一定的相似性,因此,動態規劃試圖僅僅解決每個子問題一次,從而減少計算量:一旦某個給定的子問題的解已經算出,則將其存儲,以便下次需要同一個子問題時直接查表使用。

分治與動態規劃

共同點:二者都要求原問題具有最優子結構,都是將原問題進行分解,分而治之,分解成若干個子問題,然後將子問題的解進行合併,形成原問題的解。

不同點:

1.找出最優解的性質,刻畫其結構特徵和最優子結構特徵,將原問題分解成若干個子問題;

把原問題分解為若干個子問題,子問題和原問題形式相同或類似,只不過規模變小了。子問題都解決,原問題即解決,子問題的解一旦求出就會被保存,所以每個子問題只需求解一次。

2.遞歸地定義最優值,刻畫原問題解與子問題解間的關係,確定狀態;

在用動態規劃解題時,我們往往將和子問題相關的各個變量的一組取值,稱之為一個「狀態」。一個「狀態」對應於一個或多個子問題, 所謂某個「狀態」下的「值」,就是這個「狀態」所對應的子問題的解。所有「狀態」的集合,構成問題的「狀態空間」。「狀態空間」的大小,與用動態規劃解決問題的時間複雜度直接相關。

3.以自底向上的方式計算出各個子問題、原問題的最優值,並避免子問題的重複計算;

定義出什麼是「狀態」,以及在該「狀態」下的「值」後,就要找出不同的狀態之間如何遷移――即如何從一個或多個「值」已知的 「狀態」,求出另一個「狀態」的「值」(遞推型)。

4.根據計算最優值時得到的信息,構造最優解,確定轉移方程;

狀態的遷移可以用遞推公式表示,此遞推公式也可被稱作「狀態轉移方程」。

有 N 件物品和一個容量為 V 的背包。第i件物品的費用是 c[i],價值是 w[i]。求解將哪些物品裝入背包可使這些物品的費用總和不超過背包容量,且價值總和最大。

f[i][v] 表示前 i 件物品恰放入一個容量為 v 的背包可以獲得的最大價值。則其狀態轉移方程便是:

 

將"前 i 件物品放入容量為 v 的背包中"這個子問題,若只考慮第 i 件物品的策略(放或不放),那麼就可以轉化為一個只牽扯前 i-1 件物品的問題。如果不放第 i 件物品,那麼問題就轉化為"前 i-1 件物品放入容量為 v 的背包中";如果放第 i 件物品,那麼問題就轉化為"前 i-1 件物品放入剩下的容量為 v-c[i] 的背包中」,此時能獲得的最大價值就是 f [i-1][v-c[i]] 再加上通過放入第 i 件物品獲得的價值 w[i] 。

void package(){  for(int i = 1; i <= N; i++)      {    for(int j = 1; j <= M; j++)      {      if(j >= weight[i])        {        V[i][j] = max(V[i - 1][j], V[i - 1][j - weight[i]] + value[i]);      }      else      {        V[i][j] = V[i - 1][j];      }    }  }}

二位數組的空間複雜度顯而易見為 O(nm) ,n 可能不大,但是實際應用的 w 可能會很大,這樣造成了比較大的空間佔用。而仔細觀察發現,矩陣中的值只與當前值的左上角的矩陣裡的值有關。並且是按行進行更新的,所以可以用一個一維數據就能進行狀態的更替,不過要注意更新的方向。

所以依賴關係為:下面依賴上面,右邊依賴左邊;

如果正常地從左到右邊,那麼右邊面等待更新的值需要的依賴(左邊)就會被覆蓋掉,所以應該每行從右邊開始更新。代碼如下:

void packageOpt(){  for(int i = 1; i <= N; i++)  {    for (int j = M; j >= 1; j--)    {      if (j >= weight[i])      {        V[j] = max(V[j], V[j - weight[i]] + value[i]);      }    }  }}

給定兩個字符word1和word2,找到將word1和word2匹配的最大字符,所需操作的最小步數。(每個操作計為1步)。

對單詞允許以下3種操作:

a)插入字符

b)刪除字符

c)替換字符

定義兩個字符串之間的編輯距離:從字符串str1str2的最少的操作次數。首先,編輯距離是不會大於str1.length + str2.length的(可以通過刪除操作把兩個字符串都轉化為空串)。假設求字符AB的編輯距離,考慮下面幾種情況:

如果A[i] = B[j],那麼這時候還需要操作嗎?

這個時候的刪除和替換操作只會讓情況變得更壞,而且插入操作不會使情況變得更好,只要計算  F(i, j) = F(i-1, j-1)

如果A[i] != B[j],怎麼辦呢?需要進行如下操作:

1. 刪除A串的第一個字符,然後計算  

2. 刪除B串的第一個字符,然後計算  

3. 修改A串的第一個字符為B串的第一個字符,然後計算  

4. 修改B串的第一個字符為A串的第一個字符,然後計算  

5. 增加B串的第一個字符到A串的第一個字符之前,然後計算  

6. 增加A串的第一個字符到B串的第一個字符之前,然後計算  

合併之後可以簡化為以下三步:

1. 從  

2. 從  

3. 從  

那麼此時

 

註:其中  

多步決策可如下表示:

int strSimilar(char *strA, char *strB,int n,int m){  int **A = new int *[n+1];       for(int i = 0; i< n + 1; i++)  {    A[i] = new int[m+1];  }  A[0][0] = 0;  A[1][0] = 1;  A[0][1] = 1;  for (int i = 1; i<=m; i++)  {    A[0][i] = i;      }  for (int i = 1; i<=n; i++)  {    A[i][0] = i;  }  for(int i = 0; i<n; i++)  {    for(int j= 0; j<m; j++)    {      if (strA[i] == strB[j])      {        A[i+1][j+1] = A[i][j];      }       else      {        A[i+1][j+1] = min(A[i+1][j], A[i][j+1], A[i][j]) + 1;      }    }  }  return A[n][m];    }

二維數組複雜度為  

相關焦點

  • 經典算法研究系列 之 動態規划算法
    數經典算法研究系列 之基本概念動態規划過程是:每次決策依賴於當前狀態,又隨即引起狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最優化決策解決問題的過程就稱為動態規劃。基本思想與策略基本思想與分治法類似,也是將待求解的問題分解為若干個子問題(階段),按順序求解子階段,前一子問題的解,為後一子問題的求解提供了有用的信息。在求解任一子問題時,列出各種可能的局部解,通過決策保留那些有可能達到最優的局部解,丟棄其他局部解。依次解決各子問題,最後一個子問題就是初始問題的解。
  • 動態規划算法入門,這就夠了
    動態規劃(Dynamic programming,簡稱DP),是大家都覺得比較難以掌握的算法。為了應付面試,我們經常會背誦一下斐波那楔數列或者背包問題的源碼,其實,只要理解了思想,掌握基本的模型,然後再來點寫代碼的套路,動態規劃並沒有那麼難。
  • 如何掌握動態規划算法的套路?
    實際上,它並不是不是一種確定的算法,它是一種最優化的方法求解問題的思想或方法。它是由美國數學家貝爾曼(Bellman)在研究多階段決策過程的優化問題時提出。不過,與之對應的還有一些與時間無關的靜態規劃,如:線性規劃、非線性規劃等。在運籌學中,動態規劃是的非常重要的內容,在各個行業領域都有著廣泛的應用。我們如何理解動態規劃?
  • 動態規划算法秘籍
    動態規劃是1957年理察·貝爾曼在《Dynamic Programming》一書中提出來的,八卦一下,這個人可能有同學不知道,但他的一個算法你可能聽說過,他和萊斯特·福特一起提出了求解最短路徑的Bellman-Ford 算法,該算法解決了Dijkstra算法不能處理負權值邊的問題。
  • 動態規划算法Dynamic Programming
    動態規劃與分治法相似,都是通過組合子問題的解來求解原問題。不同的是,分治法將問題劃分為互不相交的子問題,遞歸的求解子問題,再將他們的解組合起來,求出原問題的解。與之相反,動態規劃應用於子問題重疊的情況,即不同的子問題具有公共的子子問題。在這種情況下,分治法會做許多不必要的工作,他會反覆求解那些公共子子問題。
  • 五大常用算法:動態規划算法
    一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最優化決策解決問題的過程就稱為動態規劃。二、基本思想與策略基本思想與分治法類似,也是將待求解的問題分解為若干個子問題(階段),按順序求解子階段,前一子問題的解,為後一子問題的求解提供了有用的信息。
  • 豁然開朗經典算法之「動態規劃」
    《算法導論》是這樣解釋動態規劃的:動態規劃與分治法相似,都是通過組合子問題的解來求解原問題答案,將問題劃分為互不相交的子問題,遞歸的求解子問題,最後合併子問題的答案,得到原問題的答案。翻譯成人話就是:計算並存儲小問題的解,並將這些解組合成大問題的解。
  • 【算法學習】動態規劃
    今天咱們來聊聊動態規劃。動態規劃(dynamic programming)是一種基礎的算法設計思想。作為一種尋找最優解的通用方法,它是在20世紀50年代由美國數學家Richard Bellman(也就是之前最短路徑問題中Bellman ford算法的發明者之一)所發明的。
  • 如何掌握動態規划算法的套路? - 51CTO.COM
    實際上,它並不是不是一種確定的算法,它是一種最優化的方法求解問題的思想或方法。它是由美國數學家貝爾曼(Bellman)在研究多階段決策過程的優化問題時提出。不過,與之對應的還有一些與時間無關的靜態規劃,如:線性規劃、非線性規劃等。在運籌學中,動態規劃是的非常重要的內容,在各個行業領域都有著廣泛的應用。我們如何理解動態規劃?
  • 一文讀懂動態規划算法
    一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最優化決策解決問題的過程就稱為動態規劃。基本思想與策略基本思想與分治法類似,也是將待求解的問題分解為若干個子問題(階段),按順序求解子階段,前一子問題的解,為後一子問題的求解提供了有用的信息。
  • 算法思想簡介(分制),貪心(DJS)動態分配(dp)回溯(萬能)
    算法思想簡介(分制(分開在遞歸),貪心(DJS),動態分配(dp,解決多變化條件),回溯(萬能,深度優先))不管是動態規劃,還是回溯都是在可選擇 條件固定時,進行選擇 ,都會用到遞歸調用。不同的是:貪心最好理解,從頭開始找最優結果一直到最後。
  • Java動態規划算法策略
    Dynamic Programming是五大常用算法策略之一,簡稱DP,譯作中文是「動態規劃」,可就是這個聽起來高大上的翻譯坑苦了無數人,因為看完這個算法你可能會覺得和動態規劃根本沒太大關係,它對「動態」和「規劃」都沒有太深的體現。
  • 動態規划算法,3000字真正幫助你入門
    本文目標幫助朋友們認識到動態規划算法之美,從而引發學習它、研究它的興趣。一 動態規劃引言某個問題一旦找到動態規劃的解法,一般便是接近或就是最優解法,也正因為此,無數程式設計師為它著迷,大廠面試也是必考。但是,動態規劃又非常靈活,本質上沒有套路,問題不同,動態規劃的迭代方程就不同。
  • 動態規劃之 KMP 算法詳解
    為什麼我認為 KMP 算法就是個動態規劃問題呢,等會有解釋。對於動態規劃,之前多次強調了要明確dp數組的含義,而且同一個問題可能有不止一種定義dp數組含義的方法,不同的定義會有不同的解法。讀者見過的 KMP 算法應該是,一波詭異的操作處理pat後形成一個一維的數組next,然後根據這個數組經過又一波複雜操作去匹配txt。時間複雜度 O(N),空間複雜度 O(M)。
  • 拒絕遺忘:高效的動態規划算法
    不懂動態規劃的人會在解決過的問題上再次浪費時間,懂的人則會事半功倍。那麼什麼是動態規劃?這種算法有何神奇之處?本文作者給出了初步的解答。假設你正在使用適當的輸入數據進行一些計算。你在每個實例中都進行了一些計算,以便得到一些結果。當你提供相同的輸入時,你不知道會有相同的輸出。這就像你在重新計算之前已經計算好的特定結果一樣。那麼問題出在哪裡呢?
  • 我的第437篇原創:動態規划算法入門篇,真正幫助你入門!!!
    本文目標幫助朋友們認識到動態規划算法之美,從而引發學習它、研究它的興趣。一 動態規劃引言某個問題一旦找到動態規劃的解法,一般便是接近或就是最優解法,也正因為此,無數程式設計師為它著迷,大廠面試也是必考。但是,動態規劃又非常靈活,本質上沒有套路,問題不同,動態規劃的迭代方程就不同。
  • 算法篇:動態規劃(二)
    算法:本篇屬於動態規劃的進階題目,我們可以通過數據dp[i]來表示包括第i個元素的計算和
  • 總結 | 動態規劃十問十答
    動態規劃是IT技術面試中最難的算法,很多人披荊斬棘,最終還是跪在動態規劃題目上。
  • 深入理解動態規划算法 | 最長公共子序列LCS
    通過歡迎點擊「算法與編程之美」↑關注我們!
  • javascript經典算法之最小硬幣找零問題實戰
    正文筆者抽空總結了幾個比較經典且實用的算法, 最少硬幣找零問題 是本文介紹的第一道算法題:問題:給出要找零的錢數amount以及可用的硬幣面額c1, c2, c3, ..., 求所需的最少硬幣個數。動態規劃法 2. 貪心算法接下來筆者具體介紹這兩種算法的思路和實現代碼.1. 動態規劃法動態規劃的思想是把一個複雜問題分解為多個子問題,通過解決一個個子問題,再把子問題合併比較,從而解決複雜問題的思想。