• 算法导论学习-动态规划之记忆化搜索


    一. 动态规划

    动态规划(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
    ",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
    ",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
    ",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 }
  • 相关阅读:
    windows10使用记录
    DevOps理论与实践总结
    工资调整
    Node程序debug小记
    基于游标的分页接口实现
    util.promisify 的那些事儿
    如何编写 Typescript 声明文件
    async语法升级踩坑小记
    使用TS+Sequelize实现更简洁的CRUD
    使用 TypeScript 改造构建工具及测试用例
  • 原文地址:https://www.cnblogs.com/fu11211129/p/4276213.html
Copyright © 2020-2023  润新知