10 1動態規劃例題 數字三角形

2021-08-26 10:37:02 字數 4748 閱讀 8651

10.1 什麼是動態規劃

前面學過了用遞迴的方法解決問題。但是,單純的遞迴,在解決某些問題的時候,效率

會很低。例如下面這道題目:

例題:數字三角形

問題描述

73 8

8 1 0

2 7 4 4

4 5 2 6 5

上圖給出了乙個數字三角形。從三角形的頂部到底部有很多條不同的路徑。對於每條路

徑,把路徑上面的數加起來可以得到乙個和,和最大的路徑稱為最佳路徑。你的任務就是求

出最佳路徑上的數字之和。

輸入資料

輸入的第一行是乙個整數 n (1 < n <= 100),給出三角形的行數。下面的 n行給出數字

三角形。數字三角形上的數的範圍都在 0和 100之間。

輸出要求

輸出最大的和。

輸入樣例57

3 88 1 0

2 7 4 4

4 5 2 6 5

輸出樣例

30193

解題思路

這道題目可以用遞迴的方法解決。基本思路是:

以d( r, j)表示第

r行第 j 個數字(r,j都從

1開始算

),以maxsum(r, j) 代表從第 r 行

的第 j 個數字到底邊的最佳路徑的數字之和,則本題是要求 maxsum(1, 1) 。

從某個d(r, j)出發,顯然下一步只能走

d(r+1, j)或者

d(r+1, j+1)。如果走

d(r+1, j),那

麼得到的

maxsum(r, j)就是

maxsum(r+1, j) + d(r, j);如果走

d(r+1, j+1),那麼得到的

maxsum(r, j)就是

maxsum(r+1, j+1) + d(r, j)。所以,選擇往**走,就看

maxsum(r+1, j)和

maxsum(r+1, j+1)哪個更大了。程式如下:

1. #include 2. #define max_num 100 3. int d[max_num + 10][max_num + 10]; 4. int n; 5. int maxsum( int r, int j) 6. 16. main() 17.

上面的程式,效率非常低,在

n值並不大,比如

n=100的時候,就慢得幾乎永遠算不

出結果了。為什麼會這樣呢?是因為過多的重複計算。我們不妨將對

maxsum函式的一次

呼叫稱為一次計算。那麼,每次計算

maxsum(r, j)的時候,都要計算一次

maxsum(r+1, j),

而每次計算

maxsum(r, j+1)的時候,也要計算一次

maxsum(r+1, j)。重複計算因此產生。在

題目中給出的例子裡,如果我們將

maxsum(r, j)被計算的次數都寫在位置(r, j),那麼就能

得到下面的三角形:

1 11 2 1

1 3 3 1

1941 4 6 4 1

從上圖可以看出,最後一行的計算次數總和是

16,倒數第二行的計算次數總和是

8。不難總結出規律,對於

n行的三角形,總的計算次數是

20 +21+22 ……2n-1=2n。當

n= 100時,

總的計算次數是乙個讓人無法接受的大數字。

既然問題出在重複計算,那麼解決的辦法,當然就是,乙個值一旦算出來,就要記住,

以後不必重新計算。即第一次算出

maxsum(r, j)的值時,就將該值存放起來,下次再需要計

算maxsum(r, j)時,直接取用存好的值即可,不必再次呼叫

maxsum進行函式遞迴計算了。

這樣,每個

maxsum(r, j)都只需要計算

1次即可,那麼總的計算次數(即呼叫

maxsum函式

的次數)就是三角形中的數字總數,即

1+2+3+……n = n(n+1)/2

如何存放計算出來的

maxsum(r, j)值呢?顯然,用乙個二維陣列

amaxsum[n][n]就

能解決。amaxsum[r][j]就存放

maxsum(r, j)的計算結果。下次再需要

maxsum(r, j)的值時,

不必再呼叫

maxsum函式,只需直接取

amaxsum[r][j]的值即可。程式如下:

1. #include 2. #include 3. #define max_num 100 4. int d[max_num + 10][max_num + 10]; 5. int n; 6. int amaxsum[max_num + 10][max_num + 10]; 7. int maxsum( int r, int j) 8. 20. main() 21.

這種將乙個問題分解為子問題遞迴求解,並且將中間結果儲存以避免重複計算的辦法,

就叫做「動態規劃」。動態規劃通常用來求最優解,能用動態規劃解決的求最優解問題,必

須滿足,最優解的每個區域性解也都是最優的。以上題為例,最佳路徑上面的每個數字到底部

的那一段路徑,都是從該數字出發到達到底部的最佳路徑。

實際上,遞迴的思想在程式設計時未必要實現為遞迴函式。在上面的例子裡,有遞推公式:

因此,不需要寫遞迴函式,從

amaxsum[n-1]這一行元素開始向上逐行遞推,就能求得

最終amaxsum[1][1]的值了。程式如下:

1. #include 2. #include 3. #define max_num 100 4. int d[max_num + 10][max_num + 10]; 5. int n; 6. int amaxsum[max_num + 10][max_num + 10]; 7. main() 8. 23. printf("%d", amaxsum[1][1]); 24. }

10.2 動態規劃解題的一般思路

許多求最優解的問題可以用動態規劃來解決。用動態規劃解題,首先要把原問題分解為

若干個子問題,這一點和前面的遞迴方法類似。區別在於,單純的遞迴往往會導致子問題被

重複計算,而用動態規劃的方法,子問題的解一旦求出就會被儲存,所以每個子問題只需求

解一次。

子問題經常和原問題形式相似,有時甚至完全一樣,只不過規模從原來的

n變成了

n-1,

或從原來的

n×m變成了

n×(m-1) ……等等。找到子問題,就意味著找到了將整個問題逐

漸分解的辦法,因為子問題可以用相同的思路分解成子子問題,一直分解下去,直到最底層

規模最小的的子問題可以一目了然地看出解(象上面數字三角形的遞推公式中,

r=n時,解

就是一目了然的)。每一層子問題的解決,會導致上一層子問題的解決,逐層向上,就會導

致最終整個問題的解決。如果從最底層的子問題開始,自底向上地推導出乙個個子問題的解,

那麼程式設計的時候就不需要寫遞迴函式。

在用動態規劃解題時,我們往往將和子問題相關的各個變數的一組取值,稱之為乙個「狀

態」。乙個「狀態」對應於乙個或多個子問題,所謂某個「狀態」下的「值」,就是這個「狀

態」所對應的子問題的解。

具體到數字三角形的例子,子問題就是「從位於

(r,j)數字開始,到底邊路徑的最大和」。

這個子問題和兩個變數

r和j相關,那麼乙個「狀態」,就是

r, j的一組取值,即每個數字的

位置就是乙個「狀態」。該「狀態」所對應的「值」,就是從該位置的數字開始,到底邊的最

佳路徑上的數字之和。

定義出什麼是「狀態」,以及在該「狀態」下的「值」後,就要找出不同的狀態之間如

何遷移―――即如何從乙個或多個「值」已知的「狀態」,求出另乙個「狀態」的「值」。

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

如下的遞推式就說明了狀態轉移的方式:

上面的遞推式表明了如果知道了狀態(

r+1,j)和狀態(

r+1,j+1)對應的值,該如何

求出狀態(r,j)對應的值,即兩個子問題的解決,如何導致乙個更高層的子問題的解決。

所有「狀態」的集合,構成問題的「狀態空間」。「狀態空間」的大小,與用動態規劃解

決問題的時間複雜度直接相關。在數字三角形的例子裡,一共有

n×(n+1)/2個數字,所以

這個問題的狀態空間裡一共就有

n×(n+1)/2個狀態。在該問題裡每個「狀態」只需要經過

一次,且在每個狀態上作計算所花的時間都是和

n無關的常數。

用動態規劃解題,經常碰到的情況是,

k個整型變數能構成乙個狀態(如數字三角形中

的行號和列號這兩個變數構成「狀態」)。如果這

k個整型變數的取值範圍分別是

n1, n2, ……

nk,那麼,我們就可以用乙個

k維的陣列

array[n1] [n2]……[nk]來儲存各個狀態的「值」。

這個「值」未必就是乙個整數或浮點數,可能是需要乙個結構才能表示的,那麼

array就可

以是乙個結構陣列。乙個「狀態」下的「值」通常會是乙個或多個子問題的解。

用動態規劃解題,如何尋找「子問題」,定義「狀態」,「狀態轉移方程」是什麼樣的,

並沒有一定之規,需要具體問題具體分析,題目做多了就會有感覺。甚至,對於同乙個問題,

分解成子問題的辦法可能不止一種,因而「狀態」也可以有不同的定義方法。不同的「狀態」

定義方法可能會導致時間、空間效率上的區別。

動態規劃 數字三角形

如圖所示的數字三角形,從頂部出發,在每一結點可以選擇向左走或得向右走,一直走到底層,要求找出一條路徑,使路徑上的值最大。第一行是數塔層數n 1 n 100 第二行起,按數塔圖形,有乙個或多個的整數,表示該層節點的值,共有n行。輸出最大值。5 1311 8 12 7 26 6 14 15 8 12 7...

動態規劃 數字三角形

7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 在上面的數字三角形中尋找一條從頂部到底邊的路徑,使得 路徑上所經過的數字之和最大。路徑上的每一步都只能往左下或 右下走。只需要求出這個最大和即可,不必給出具體路徑。三角形的行數大於1小於等於100,數字為 0 99 5 三角形行數。下面是三...

動態規劃 數字三角形

在用動態規劃解題時,我們往往將和子問題相關的各個變數的一組取值,稱之為乙個 狀態 乙個 狀態 對應於乙個或多個子問題,所謂某個 狀態 下的 值 就是這個 狀態 所對應的子問題的解。以 數字三角形 為例,初始狀態就是底邊數字,值就是底邊數字值。定義出什麼是 狀態 以及在該 狀態 下的 值 後,就要找出...