LeetCode刷题——63. 不同路径 II

题目

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

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

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

在这里插入图片描述

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

说明:m 和 n 的值均不超过 100。

示例 1:

输入:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-paths-ii

思路

该题是上一题62. 不同路径的升级版,多了一个障碍物而已。可以参照上一题的解题思路。

只要将经过障碍物的路径数设成0即可。

代码

代码都是基于62. 不同路径修改而来的。

递归

class Solution(object):
    def unique(self, obstacleGrid, m, n):
        # 如果遇到障碍物,返回0
        if obstacleGrid[n][m] == 1:
            return 0

		 # 如果到达了起点,就是一种解法
        if m == 0 and n == 0:
            return 1

        ways = 0

        # 如果可以向左回退一步
        if m >= 1:
            ways = self.unique(obstacleGrid, m - 1, n)
        # 如果可以向上回退一步
        if n >= 1:
            ways += self.unique(obstacleGrid, m, n - 1)

        return ways

    def uniquePathsWithObstacles(self, obstacleGrid):
        """
        :type obstacleGrid: List[List[int]]
        :rtype: int
        """
        # n行 m 列的列表
        n, m = len(obstacleGrid), len(obstacleGrid[0])

        return self.unique(obstacleGrid, m - 1, n - 1)

为了能使用上一题的代码,n, m = len(obstacleGrid), len(obstacleGrid[0])这里求出了这个列表的行数与列数,并且传入递归函数中。

if obstacleGrid[n][m] == 1:
	return 0

基本在递归函数中只加了这段代码而已,非常简单。

在这里插入图片描述

虽然思路是对的,但是递归的方式是比较低效的,这里导致了计算超时。参阅LeetCode刷题之动态规划思想,我们可以将其改成记忆化搜索的方式,解决重叠子问题。

记忆化搜索

dp = {}
class Solution(object):
    def unique(self, obstacleGrid, m, n):
        if (m,n) not in dp:
            # 如果遇到障碍物,返回0
            if obstacleGrid[n][m] == 1:
                dp[(m,n)] = 0
                return 0
                
        	# 如果到达了起点,就是一种解法
            if m == 0 and n == 0:
                dp[(m,n)] = 1
                return 1

            ways = 0

            # 如果可以向左回退一步
            if m >= 1:
                ways = self.unique(obstacleGrid, m - 1, n)
            # 如果可以向上回退一步
            if n >= 1:
                ways += self.unique(obstacleGrid, m, n - 1)

            dp[(m,n)] = ways

        return dp[(m,n)]

    def uniquePathsWithObstacles(self, obstacleGrid):
        # n行 m 列的列表
        n, m = len(obstacleGrid), len(obstacleGrid[0])

        return self.unique(obstacleGrid, m - 1, n - 1)

记忆化搜索的修改方式也很容易。

在这里插入图片描述
但是又说我记忆化搜索的代码错误了,实际上进行测试时是对的。

在这里插入图片描述
不管,改成动态规划。

动态规划

class Solution(object):
    def uniquePathsWithObstacles(self, obstacleGrid):
        """
        :type obstacleGrid: List[List[int]]
        :rtype: int
        """
        # n行 m 列的列表
        n, m = len(obstacleGrid), len(obstacleGrid[0])

        # n行 m 列的列表
        dp = [[0] * m for _ in range(n)]

        #做一个小优化,如果起点就是障碍,那么无解
        if obstacleGrid[0][0] == 1: 
            return 0
        else:
            dp[0][0] = 1

        # 先设定好边界值,设置第一列的边界值
        for i in range(1, n):
            # 如果当前格子不是是障碍 且上一个格子可达
            if obstacleGrid[i][0] != 1 and dp[i - 1][0] != 0 :
                dp[i][0] = 1
            # 否则当前格子不可达

        # 设置第一行的边界值
        for j in range(1, m):
            if obstacleGrid[0][j] != 1 and dp[0][j - 1] != 0:
                dp[0][j] = 1

        for i in range(1, n):
            for j in range(1, m):
                # 如果当前格不是障碍,才能计算
                if obstacleGrid[i][j] != 1:
                    # dp[i][j] = 左边一步加上面一步的结果
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1]

        return dp[-1][-1]

这里设定边界值的时候要麻烦一点。

在这里插入图片描述
如上图所示,用"X"表示障碍物,如果障碍物出现在了边界上,障碍物和右边的格子都是不可达的。
考虑到这个情况就可以了。

#做一个小优化,如果起点就是障碍,那么无解
if obstacleGrid[0][0] == 1: 
    return 0
else:
    dp[0][0] = 1

并且增加对起点的判断,使得下面设定边界值的代码for循环的range都可以从1开始:

# 先设定好边界值,设置第一列的边界值
for i in range(1, n):
    # 如果当前格子不是是障碍 且上一个格子可达
    if obstacleGrid[i][0] != 1 and dp[i - 1][0] != 0 :
        dp[i][0] = 1
    # 否则当前格子不可达

# 设置第一行的边界值
for j in range(1, m):
    if obstacleGrid[0][j] != 1 and dp[0][j - 1] != 0:
        dp[0][j] = 1

在这里插入图片描述

原创文章 176 获赞 286 访问量 18万+

猜你喜欢

转载自blog.csdn.net/yjw123456/article/details/106024447