算法導論學習-動態規划之記憶化搜索


一. 動態規划

動態規划(dynamic programming),與“分治思想”有些相似,都是利用將問題分 為子問題,並通過合並子問題的解來獲得整個問題的解。於“分治”的不同之處在 於,對於一個相同的子問題動態規划算法不會計算第二次,其實現原理是將每一個計算過的子問題的值保存在一個表中。

二. 記憶化搜索

我們常見的動態規划問題,比如流水線調度問題,矩陣鏈乘問題等等都是“一步接着一步解決的”,即規模為 i 的問題需要基於規模 i-1 的問題進行最優解選擇,通常的遞歸模式為DP(i)=optimal{DP(i-1)}。而記憶化搜索本質上也是DP思想,當子問題A和子問題B存在子子問題C時,如果子子問題C的最優解已經被求出,那么子問題A或者是B只需要“查表”獲得C的解,而不需要再算一遍C。記憶化搜索的DP模式比普通模式要“隨意一些”,通常為DP(i)=optimal(DP(j)), j < i。

三. 滑雪問題

上圖顯示為R*C的雪場,R是行數,C是列數。圓圈內的數字表示的是雪場的海拔高度h,根據常識我們知道,滑雪只有從上往下滑行才可能滑的動,現在我們想要求出能夠滑行的最長距離,上面的例子我們可以很直接判斷出25-24-......-1這個順時針方向螺旋的滑雪方式可以滑的最遠。

那么這個問題如何用編程來實現呢?我們發現這是一個典型的遞推,DP(i, j)表示從坐標(i,j)出發所能滑行的最大長度,且有:DP(i, j)=optimal{DP(i±1, j±1)}+1。下面貼上源代碼。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int max_size=110;
 7 int R,C;
 8 int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
 9 int h[max_size][max_size],dp[max_size][max_size];
10 int inMap(int x,int y){
11     if(x>=0&&x<=R-1&&y>=0&&y<=C-1) return 1;
12     return 0;
13 }
14 int max2(int a,int b,int c,int d){
15     return max(max(a,b),max(c,d));
16 }
17 int dfs(int i,int j){
18     int nx,ny,down=0,up=0,left=0,right=0;
19     if(dp[i][j]) return dp[i][j];
20     nx=i+dir[0][0]; ny=j+dir[0][1];
21     if(inMap(nx,ny)){
22         if(h[i][j]>h[nx][ny]) up=dfs(nx,ny);
23     }
24     nx=i+dir[1][0]; ny=j+dir[1][1];
25     if(inMap(nx,ny)){
26         if(h[i][j]>h[nx][ny]) right=dfs(nx,ny);
27     }
28     nx=i+dir[2][0]; ny=j+dir[2][1];
29     if(inMap(nx,ny)){
30         if(h[i][j]>h[nx][ny]) down=dfs(nx,ny);
31     }
32     nx=i+dir[3][0]; ny=j+dir[3][1];
33     if(inMap(nx,ny)){
34         if(h[i][j]>h[nx][ny]) left=dfs(nx,ny);
35     }
36     dp[i][j]=max2(up,down,left,right)+1;
37     return dp[i][j];
38 }
39 int main(){
40     scanf("%d%d",&R,&C);
41     memset(h,0,sizeof(h));
42     memset(dp,0,sizeof(dp));
43     for(int i=0;i<R;i++){
44         for(int j=0;j<C;j++){
45             scanf("%d",&h[i][j]);
46         }
47     }
48     int ans=-1;
49     for(int i=0;i<R;i++){
50         for(int j=0;j<C;j++){
51             ans=max(ans,dfs(i,j));
52         }
53     }
54     printf("%d\n",ans);
55 }
View Code

四. 切棒子問題

給你一根長n英尺的棒子和一份關於該棒子的價目表如下(其中 i = 1,2,3,…,n),請問如何將這根棒子賣出最高的價格,可以對棒子進行切割。

這個題同樣是可以利用DP記憶化搜索來實現的,遞推公式為DP(n)=optimal{max{price(i)+DP(n-i)|1≤i≤n}}。實現代碼如下:

1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int max_size=50;
 6 const int inf=1<<30;
 7 int price[max_size],dp[max_size];
 8 int n;
 9 int dfs(int n){
10     if(dp[n]) return dp[n];
11     if(n==0) return 0;
12     int mmax=-inf;
13     for(int i=1;i<=n;i++){
14         mmax=max(mmax,price[i]+dfs(n-i));
15     }
16     dp[n]=mmax;
17     return dp[n];
18 }
19 int main(){
20     while(scanf("%d",&n)!=EOF){
21         for(int i=1;i<=n;i++) scanf("%d",&price[i]);
22         printf("%d\n",dfs(n));
23     }
24 }
View Code

五. 01背包問題

問題描述: 有N件物品和一個重量為M的背包。(每種物品均只有一件)第i件物品的重量是w[i],價值是p[i]。求解將哪些物品裝入背包可使價值總和最大。思路也很簡單,直接看代碼

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int max_size=50;
 6 const int inf=1<<30;
 7 int p[max_size],w[max_size],dp[max_size][max_size];
 8 int n,v;
 9 int dfs(int i,int v){
10     if(dp[i][v]) return dp[i][v];
11     if(i==0||v<=0) return 0;
12     if(w[i]>v) dp[i][v]=dfs(i-1,v);
13     else dp[i][v]=max(dfs(i-1,v),dfs(i-1,v-w[i])+p[i]);
14     return dp[i][v];
15 }
16 int main(){
17     while(scanf("%d",&n)!=EOF){
18         memset(dp,0,sizeof(dp));
19         for(int i=1;i<=n;i++) scanf("%d",&p[i]);
20         for(int i=1;i<=n;i++) scanf("%d",&w[i]);
21         scanf("%d",&v);
22         printf("%d\n",dfs(n,v));
23     }
24 }
View Code

六. 總結

通過前兩個例子分析,我們可以得出DP記憶化搜索的算法模板(自己DIY的,大家可以選擇參考)

1 dfs(problem a){
2     if(a has been solved) 
3         then: consult the record.
4     else//get the optimal solution of problem a.
5         divide the problem a into several sub-problems(a1,a2,...,ak)
6         get the solution of problem a by dfs(a1),dfs(a2),...,dfs(ak).
7     finally write the optimal solution into record.
8 }

 


免責聲明!

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



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