【Leetcode 动态规划、深度优先搜索】不同路径(62)、 不同路径 II(63)、不同路径 III(980)
题目:不同路径 II(63)
一个机器人位于一个 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. 向下 -> 向下 -> 向右 -> 向右
解答
# 动态规划转换方程:
# ① dp[i][j] = dp[i-1][j] + dp[i][j-1] (i>0 and j>0)
# ② dp[i][j] = 1 ((i=0 and dp[i][j-1]!=0) or (j=0 and dp[i-1][j]!=0)), 处理边界, 要求到(i,j)前一个位置路径数不是0, 否则就代表这条路被阻断了
# dp[i][j]表示能到(i, j)位置的路径条数. 条件②的dp[i][j-1]!=0、dp[i-1][j]!=0是必须的,否则有反例:[[0, 0], [1, 1], [0, 0]]
class Solution:
# 二维dp
def uniquePathsWithObstacles(self, nums):
if not nums or nums[0][0] == 1:
return 0
rows = len(nums)
cols = len(nums[0])
dp = [[0 for _ in range(cols)] for _ in range(rows)]
for i in range(rows):
for j in range(cols):
if i == 0 and j == 0: # 处理左上角
dp[i][j] = 1
continue
if nums[i][j] == 1: # 障碍物
dp[i][j] = 0
continue
if (j == 0 and dp[i-1][j] != 0) or (i == 0 and dp[i][j-1] != 0): # 处理左、上边界
dp[i][j] = 1
else:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
# # 深搜超时,暴风哭泣辽。。
# class Solution:
# def __init__(self):
# self.rows = 0
# self.cols = 0
# self.book = [[]]
# self.nums = [[]]
# self.cnt = 0
# self.next = [
# [0, 1],
# [1, 0]
# ]
#
# def uniquePathsWithObstacles(self, obstacleGrid):
# if not obstacleGrid:
# return 0
# self.nums = obstacleGrid
# self.rows = len(obstacleGrid)
# self.cols = len(obstacleGrid[0])
#
# self.book = [[0 for _ in range(self.cols)] for _ in range(self.rows)]
# self.book[0][0] = 1
# self.dfs(0, 0)
# return self.cnt
#
# def dfs(self, x, y):
# if x == self.rows-1 and y == self.cols-1:
# self.cnt += 1
# return
# for i in range(2):
# tx = x + self.next[i][0]
# ty = y + self.next[i][1]
# if 0 <= tx < self.rows and 0 <= ty < self.cols and self.book[tx][ty] == 0 and self.nums[tx][ty] == 0:
# self.book[tx][ty] = 1
# self.dfs(tx, ty)
# self.book[tx][ty] = 0
题目:不同路径(62)https://leetcode-cn.com/problems/unique-paths/
不存在障碍物,很简单,动态规划嗖嗖的
# 动态规划转换方程:
# ① dp[i][j] = dp[i-1][j] + dp[i][j-1] (i>0 and j>0)
# ② dp[i][j] = 1 (i=0 or j=0), 处理边界
# dp[i][j]表示能到(i, j)位置的路径条数
class Solution:
# 二维dp
def uniquePaths(self, rows, cols):
if rows == cols == 0:
return 0
dp = [[0 for _ in range(cols)] for _ in range(rows)]
for i in range(rows):
for j in range(cols):
if i == 0 and j == 0: # 处理左上角
dp[i][j] = 1
continue
if i == 0 or j == 0: # 处理边界
dp[i][j] = 1
else:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
# 深度优先搜索也OK,不写了,参考不同路径II
题目:不同路径 III(980)https://leetcode-cn.com/problems/unique-paths-iii/
可以移动方向改为上、下、左、右了。还有一点要特别注意:每一个无障碍方格都要通过一次
深搜代码如下:
# 深度优先搜索
class Solution:
def __init__(self):
self.rows = 0
self.cols = 0
self.book = [[]]
self.nums = [[]]
self.cnt = 0
self.req_step = 0 # 要求每条路径的步数 = 无障碍方格数
self.next = [
[0, 1],
[1, 0],
[0, -1],
[-1, 0]
]
def uniquePathsIII(self, obstacleGrid):
if not obstacleGrid or obstacleGrid[0][0] == -1:
return 0
self.nums = obstacleGrid
self.rows = len(obstacleGrid)
self.cols = len(obstacleGrid[0])
self.book = [[0 for _ in range(self.cols)] for _ in range(self.rows)]
for i in range(self.rows):
for j in range(self.cols):
if self.nums[i][j] == 1: # 起点
start_x = i
start_y = j
if self.nums[i][j] != -1: # 统计一共要走多少步
self.req_step += 1
self.book[start_x][start_y] = 1
self.dfs(start_x, start_y, 1)
return self.cnt
def dfs(self, x, y, step):
if self.nums[x][y] == 2 and step == self.req_step: # 到达终点,并且每一个无障碍方格都通过一次
self.cnt += 1
return
for i in range(4):
tx = x + self.next[i][0]
ty = y + self.next[i][1]
if 0 <= tx < self.rows and 0 <= ty < self.cols and self.book[tx][ty] == 0 and self.nums[tx][ty] != -1:
self.book[tx][ty] = 1
self.dfs(tx, ty, step+1)
self.book[tx][ty] = 0