二维空间的dp详解(自顶向下and自底向上)

例题:

准备了两道做法几乎相同的二维空间的dp题目

题目一:LeetCode-64最小路径和
Leetcode64
题目二:蓝桥杯-跳跃
跳跃

两者区别只在于子问题的个数,题目一的个数只有2个方向,而题目二有9个方向。

自顶向下的好处

  1. 有助于理解dfs、bfs等等算法。
  2. 相比自底向上来说,前期虽然痛苦,但是当你清楚结果体系,熟悉后,你会发现写dp递归法比递推法写的快得多。
  3. 容错性大,就是用递推方法的话,可能会被一些判断的小错误绊住脚,而递归方法只要base case不出差错,一般没有问题。

自顶向下的函数结构详解:

dp函数的结构(可结合代码画递归树理解)无非就是:

  • 递归树的叶子结点的跳出条件->对应base case(决定子问题的深度(个数))、base case一般为最简单的题解子状态
  • 递归函数的主体->对应状态转移方程(递归树的展开)。
  • 非叶子结点(子问题)的跳出条件->将return结果写在递归树展开的后面即可 总的过程:通过状态转移方程不断将递归树延伸,直到触及base case停止延伸,往前执行之前的递归,这样就完成了每个非叶子结点的结果跳出,最终得到题解。

但要是这样写的话很快就会超时,原因是:递归树上可能大量结点重复计算,这个时候就需要一个备忘录一样的东西将以及计算好的题解保存,可将备忘录添加到base case中来,实际上我说的base case决定递归树的深度不完全正确,准确来说应该是决定子树的深度,因为递归过程实际上是一个入栈和出栈的过程,所以首先计算的是最深的那颗树。

划重点:

递归的结构:

  1. base case (决定递归树深度,叶子结点的结果)
  2. 状态转移方程(决定递归树的延伸方式)
  3. 处理完子问题的返回(决定非叶子结点的处理结果)

递归的原理:

这方面也没办法讲的明白,递归实际上就是一个后进先出的栈的形式,即递归就是入栈和出栈的过程,根据递归树结合深搜理解是最好的。

题目一(强烈建议用递归法):

DP函数递归解决:

int dp(vector<vector<int>>& grid,vector<vector<int>>&memo,int x,int y){
      if(x<0||y<0) return 666;
      //base case:
      if(x==0&&y==0) return grid[x][y];
      if(memo[x][y]!=-1) return memo[x][y];
      
      //condition transfer:
      int a = min(dp(grid,memo,x-1,y),dp(grid,memo,x,y-1));
      //更新备忘录
        memo[x][y] = a + grid[x][y];
    return memo[x][y];
  }     

DP递推解决:

int dp(vector<vector<int>>& grid) {
        int dx[] = {-1,0};
        int dy[] = {0,-1};
        int x,y;
        for(int i=0;i<grid.size();i++){
            for( int j=0;j<grid[i].size();j++){
                int tmp = INT_MAX;
                for(int k =0;k<2;k++){
                    x = i+dx[k];y=j+dy[k];
                    if(x>=0&&y>=0){
                        tmp = min(tmp,grid[x][y]);
                    }
                }
                if(tmp!=INT_MAX) 
                grid[i][j] += tmp;
            }
        }
        return grid[grid.size()-1].back();
    }

题目二(同样建议用递归法)

DP递归:

int dp(vector<vector<int>>&grid,vector<vector<int>>&memo,int x,int y){
int dx[] = {0,0,0,-1,-1,-1,-2,-2,-3};
int dy[] = {-1,-2,-3,0,-1,-2,0,-1,0};
//base case:
if(x<0||y<0)return INT_MIN;
if(x==0&&y==0)return grid[0][0];
if(memo[x][y]!=INT_MIN)return memo[x][y];
//condition transfer:
int temp = INT_MIN;
for(int i=0;i<9;i++){
    int q = dp(grid,memo,x+dx[i],y+dy[i]);
    if(q==INT_MIN)continue;
    temp = max(temp,q);
}//备忘录memo更新
memo[x][y] = temp + grid[x][y];
return memo[x][y];
}

DP递推:

int dp(vector<vector<int>>&grid){
int n =grid.size();
int m =grid[0].size();
  int x[9] = {0,0,0,-1,-1,-1,-2,-2,-3};
  int y[9] = {-1,-2,-3,0,-1,-2,0,-1,0};
  for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    {
      int temp = INT_MIN;
      for(int t=0;t<9;++t)
      {
        if(i+x[t]>0 && j+y[t]>0){
          temp = max(temp,grid[i+x[t]][j+y[t]]);
        }
      }
      if(trans!=INT_MIN) grid[i][j]+=temp;
    }
    return grid[n-1][m-1];
    }
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值