当前位置: 首页 > article >正文

不同路径 II(力扣LeetCode)动态规划

不同路径 II

题目描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。

示例 1:

在这里插入图片描述
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:

  • 向右 -> 向右 -> 向下 -> 向下
  • 向下 -> 向下 -> 向右 -> 向右

示例 2:

在这里插入图片描述
输入:obstacleGrid = [[0,1],[0,0]]
输出:1

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j] 为 0 或 1

动规五部曲:

  1. 确定dp数组(dp table)以及下标的含义
    dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。
  2. 确定递推公式
    递推公式和62.不同路径⼀样,dp[i][j] = dp[i - 1][j] + dp[i][j - 1]。
    但这⾥需要注意⼀点,因为有了障碍,(i, j)如果就是障碍的话应该就保持初始状态(初始状态为0)。
    所以代码为:
if (obstacleGrid[i][j] == 0) { // 当(i, j)没有障碍的时候,再推导dp[i][j]
	dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
  1. dp数组如何初始化
    在不同路径不同路径中我们给出如下的初始化:
vector<vector<int>> dp(m, vector<int>(n, 0)); // 初始值为0
for (int i = 0; i < m; i++) dp[i][0] = 1;
for (int j = 0; j < n; j++) dp[0][j] = 1;

因为从(0, 0)的位置到(i, 0)的路径只有⼀条,所以dp[i][0]⼀定为1,dp[0][j]也同理。
但如果(i, 0) 这条边有了障碍之后,障碍之后(包括障碍)都是⾛不到的位置了,所以障碍之后的dp[i][0]应该还是
初始值0。
如图:
在这里插入图片描述
下标(0, j)的初始化情况同理。
所以本题初始化代码为:

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循环的终⽌条件,⼀旦遇到obstacleGrid[i][0] == 1的情况就停⽌dp[i][0]的赋值1的操作,dp[0][j]同理
4. 确定遍历顺序
从递归公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1] 中可以看出,⼀定是从左到右⼀层⼀层遍历,这样保证推导dp[i][j]的时候,dp[i - 1][j] 和 dp[i][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];
	}
}
  1. 举例推导dp数组
    拿示例1来举例如题:
    在这里插入图片描述
    对应的dp table 如图:
    在这里插入图片描述
    如果这个图看不懂,建议再理解⼀下递归公式,然后照着⽂章中说的遍历顺序,⾃⼰推导⼀下!

力扣提交代码

c++

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) //如果在起点或终点出现了障碍,直接返回0
			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语言

int uniquePathsWithObstacles(int** obstacleGrid, int obstacleGridSize, int* obstacleGridColSize) 
{
    int n=obstacleGridSize;// 定义障碍物网格行数
    int m=obstacleGridColSize[0];// 定义障碍物网格列数
    //如果在起点或终点出现了障碍,直接返回0
    if(obstacleGrid[0][0]==1||obstacleGrid[n-1][m-1]==1)    return 0;

    int i,j;
    int dp[110][110]={0};//所有元素先初始化为0
    //初始化dp数组
    for(i=0;i<n&&obstacleGrid[i][0]==0;i++) dp[i][0]=1;//第一行如果遇到障碍物,则后面为0
    for(j=0;j<m&&obstacleGrid[0][j]==0;j++) dp[0][j]=1;//第一列如果遇到障碍物,则后面为0

    for(i=1;i<n;i++)
    {
        for(j=1;j<m;j++)
        {
            if(obstacleGrid[i][j]==1)   continue;//遇到障碍物就跳过继续
            dp[i][j]=dp[i-1][j]+dp[i][j-1];
        }
    }
    return dp[n-1][m-1];
}

http://www.kler.cn/a/150126.html

相关文章:

  • 计算机网络:网络层 —— 网络地址转换 NAT
  • 【深度学习基础】深入理解 卷积与卷积核
  • node.js模块化分析
  • 【Grafana】Grafana 基础入门
  • Vehicle OS软件平台解决方案
  • 软件对象粒度控制与设计模式在其中作用的例子
  • 荒野大镖客提示找不到emp.dll文件的5个修复方法-快速修复dll教程
  • ZYNQ_project:lcd_pic_400x400
  • springboot 返回problem+json
  • 【云备份】第三方库的认识与使用
  • go模版引擎的使用~~
  • 【c语言】二维数组的对角线对称交换
  • LeetCode 60. 排列序列【数学,逆康托展开】困难
  • ⑤【Sorted Set】Redis常用数据类型: ZSet [使用手册]
  • WordPress更改文章分类插件
  • CH01_适应设计模式
  • 网络安全如何自学?
  • 深圳市东星制冷机电受邀莅临2024国际生物发酵展,济南与您相约
  • Spring的依赖注入,依赖注入的基本原则,依赖注入的优势
  • 【Vulnhub 靶场】【Coffee Addicts: 1】【简单-中等】【20210520】
  • 01_原理-事件循环
  • docker (简介、dcoker详细安装步骤、容器常用命令)一站打包- day01
  • [密码学]DES
  • 二维数值型数组例题2
  • k8s中批量处理Pod应用的Job和CronJob控制器介绍
  • 【机器学习 | 可视化系列】可视化系列 之 决策树可视化