力扣62,69动态规划

文章介绍了动态规划在解决不同路径问题中的应用,包括确定dp数组含义、递推公式、初始化策略,以及处理有障碍物的版本。通过实例演示了如何使用动态规划算法求解从左上角到右下角的路径数量,强调了动态规划在解决此类问题中的重要性。
摘要由CSDN通过智能技术生成

今天我们来初步入门一下动态规划,来讲一下力扣非常经典的两个题,也是动态规划的入门题,不同路径1和2。

关于动态规划五部曲

  1. 确定dp数组含义
  2. 初始化
  3. 递推公式
  4. 遍历顺序
  5. 打印答案

 相信大家已经并不陌生,我们来先看一下比较简单的不同路径1

先看题目

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

看到题目有些同学就想到直接dfs,显然dfs肯定是可以的,但提交就会超时,所以在此我们不做过多讲解,今天我们做这个题的思路是动态规划,既然是动态规划,我们就要按照流程走。首先,我们确定dp数组含义,题目显而易见要用一个二维的dp数组来存储每一个格子的状态,含义就是走到这个点一共有多少种方法,接下来看一下递推公式,由于每次只能是向下或者向右移动,所以可以确定我们的递推公式

dp[i][j]=dp[i-1][j]+dp[i][j-1]

看到这时候肯定有同学对此有些疑惑,为什么不是dp[i-1][j]+1呢?注意了我们要求的是有多少种不同的方式,而不是一共要走多少步。

接下来就是初始化,这也是很多同学在这个题犯错的一点。可以注意到我们要推到dp[i][j]的状态必须知道其上方和右方的状态,所以第一行和第一列必须全部初始化,初始化为多少呢?很多同学就有下面的想法

for(int i=0;i<n;i++){
            dp[0][i]=i;
        }
        for(int j=0;j<m;j++){
            dp[j][0]=j;
        }

这显然是错误的,而错误的点也很显而易见,就是忘记了我们要求的是路径而不是步数,所以正确的初始化应该全部初始化为1

for(int i=0;i<n;i++){
            dp[0][i]=1;
        }
        for(int j=0;j<m;j++){
            dp[j][0]=1;
        }

就此我们动态规划五部曲已经完成了,下面放出ac代码

​
class Solution {
public:
    int uniquePaths(int m, int n) {
        int dp[m][n];
        for(int i=0;i<n;i++){
            dp[0][i]=1;
        }
        for(int j=0;j<m;j++){
            dp[j][0]=1;
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
};

​

而不同路径2的基本思路和1没什么不同,就是加了障碍,也就是加了几个判断条件,我们先来看题目

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

首先看到障碍物我们应该想到,是不是障碍物在某个地方会导致直接走不通?答案就是在起点和终点如果有障碍物,我们直接返回值0。

dp数组的含义不做过多说明,对于递推公式,大体和第一题是差不多的,但是注意如果有障碍物,我们就走不通了,也就是说,如果这个点有障碍物,我们就直接视为0,这样的话假如他的右面dp[i+1][j]就是0+dp[i+1][j-1]。

 for(int i=1;i<m;i++){
                for(int j=1;j<n;j++){
                    if (obstacleGrid[i][j] == 1) continue;
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }
            }

初始化我们依旧是仅需要初始化第一列和第一行,但是要注意,在第一列和第一行,一旦遇到了障碍物,后面的就直接全部视为0,相信这个是比较好理解的,因为只要前面有障碍物,就没有路径能到达,来看一下初始化代码

for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;
        for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;

遍历顺序和打印我们就不多说了,和上面的一个样。来看一下AC代码

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
            int m = obstacleGrid.size();
            int n = obstacleGrid[0].size();
            if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1) 
            return 0;
           vector<vector<int>> dp(m, vector<int>(n, 0));
        for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;
        for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;
            for(int i=1;i<m;i++){
                for(int j=1;j<n;j++){
                    if (obstacleGrid[i][j] == 1) continue;
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }
            }

        return dp[m-1][n-1];
    }
};

上面两题还是比较重要的,是动态规划的入门题,今年也有几个公司面试的时候出手撕了,所以大家一定要会做。

今后我会继续更新力扣(主要针对于面试),蓝桥杯,c++高性能服务器,unity,计算机网络和数据结构的知识。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值