1. 程式人生 > >對動態規劃算法的理解及相關題目分析

對動態規劃算法的理解及相關題目分析

自底向上 esp 它的 解包 宋體 成了 -h temp ace

1、對動態規劃算法的理解

(1)基本思想:

動態規劃算法的基本思想與分治法類似:將待求解的問題分解成若幹個子問題,先求解子問題,然後從這些子問題的解中得到原問題的解。但是,與分治法不同的是,為了避免重復多次計算子問題,動態規劃算法用一個表記錄所有已解決的子問題的答案,不管該子問題以後是否被利用,只要它被計算過,就將其結果填入表中。

(2)設計動態規劃算法的步驟:

①找出最優解的性質,並刻畫其結構特征

②遞歸地定義最優值

③以自底向上的方式計算最優值

④根據計算最優值時得到的信息構造最優解

(3)動態規劃算法的基本要素

最優子結構性質

當問題的最優解包含了子問題的最優解時,稱該問題具有最優子問題結構。在動態規劃算法中,利用問題的最優子結構性質,以自底向上的方式遞歸地從子問題的最優解逐步構造出整個問題的最優解。

重疊子問題性質

在用遞歸算法自頂向下解決問題時,每次產生的子問題並不總是新問題,有些子問題可能被反復計算多次。動態規劃算法利用這種子問題的重疊性質,對每一個子問題只解一次,而後將其解保存在一個表格中,當再次需要解此子問題時,只需要簡單地用常數時間查看一下結果即可。

從一般意義上講,問題所具有的最優子結構性質和重疊子問題性質是該問題可用動態規劃算法求解的基本要素。

備忘錄方法

備忘錄方法是動態規劃算法的變形。與動態規劃算法一樣,備忘錄方法用表格保存已解決的子問題的答案,在下次解決問題時,只要簡單地查看該子問題的解答,不必再次計算。與動態規劃算法不同的是,備忘錄方法的遞歸方式是自頂向下的,而動態規劃算法則是自底向上的。

備忘錄方法為每個子問題建立一個記錄項,初始化時,該記錄項存入一個特殊的值,表示該子問題尚未求解。在求解的過程中對每個待求解的子問題,首先查看相應的記錄項。若記錄項存儲的是初始化的特殊值,則需要計算該子問題的解,並保存在相應的記錄項中已備以後查看。若記錄項存儲的不是初始值,則表示該子問題已被計算過,此時記錄項存儲的值即該子問題的解答。

2、例題的遞歸方程分析

(1)租用遊艇問題:長江遊艇俱樂部在長江上設置了n個遊艇出租站1,2,…,n。遊客可在這些遊艇出租站租用遊艇,並在下遊的任何一個遊艇出租站歸還遊艇。遊艇出租站i到遊艇出租站j之間的租金為r(i,j),1<=i<j<=n。試設計一個算法,計算出從遊艇出租站1 到遊艇出租站n所需的最少租金。

 1 #include <iostream>
 2 using namespace std;
 3 int main()
 4 {
 5     int n;//n代表遊艇出租站的數量
 6     int cost[100][100];//cost[i][j]表示第i站到第j站所需要的租金
 7     cin>>n;
 8     for(int i=1;i<n;i++) {
 9         for(int j=i+1;j<=n;j++){
10             cin>>cost[i][j];//輸入i->j站的價格
11         }
12     }
13     for(int m=2;m<n;m++) {//m表示要經過的出租站點
14         for(int i=1;i<=n-m;i++) {//i表示起始出租站,保證最後一段為m,所以循環截止到i<=n-m;
15             int j = i+m;//j表示要到達的出租站,而起始站為i,故j=i+m
16             for(int t=i+1;t<=j;t++) {
17                 int temp = cost[i][t] + cost[t][j];//計算i->t站和t->j站租金之和
18                 if(cost[i][j] > temp) //比較直接從i->j站的租金便宜還是從i->t、再從t->j站便宜
19                     cost[i][j] = temp;//交換成較便宜的租金價格,直至最後第一排最後一個的值為最小租金
20             }
21         }
22     }
23     cout<<cost[1][n];
24     return 0;
25 } 

(2)單調遞增最長子序列:設計一個O(n2)時間的算法,找出由n個數組成的序列的最長單調遞增子序列的長度。

 1 int longest(int a[], int n)
 2 {//求最長單調遞增子序列的長度,形參是長度為n的數組a
 3     int length[n];//數組length[i]用於在數組a中記錄比a[i]小的數的個數
 4     int max = 1;//表示最長長度,初始化為1
 5     for (int i = 0; i < n; i++){
 6         length[i] = 1;//數組length初始化為1
 7     }
 8     for (int i = 1; i < n; i++) {
 9         for (int j = 0; j < i; j++) {
10             if (a[j] < a[i] && length[j] + 1 >length[i]) {//如果前一個數j小於後一個數i
11                 length[i] = length[j] + 1;//則length數組中i對應位置的值變成j對應位置的值加1,表示存在多一個小於a[i]的數
12                 if (max < length[i]) {
13                     max = length[i];//將值賦給max
14                 }
15             }
16         }
17     }
18     return max;//返回最長的長度
19 }

3、說明結對編程情況

一開始並不清楚怎麽運用動態規劃算法來分析和完成題目,於是和小夥伴一起認真地討論題目的內容,研究了到底什麽是動態規劃算法,再盡量地去理解它的思想之後,和兩個小夥伴一起完成這兩道編程題。在這個過程中,照著自己的理解、小夥伴們的討論以及借鑒了別的同學的建議和提醒,最終順利地完成了任務。

對動態規劃算法的理解及相關題目分析