[一] 不同路径1,图中无障碍
在判断一个问题能不能使用动态规划解决时,首先要判断:
1. 每个阶段的状态或值是否能通过上一阶段的状态或值推导出来 (满足)
2. 每个阶段的状态或值一旦决定,是不受后面阶段的状态或值影响的 (满足)
3. 是否有重复子问题的计算 (满足)
4. 是否有边界 (满足)
下面仔细讲上述4个点:
1.。 每个阶段的状态或值是否能通过上一阶段的状态或值推导出来
由于题目说,机器人只能往右或者往下走。设置 d(i,j)表示 从起点走到 i 列 j 行位置总共的路径数。则d(i,j)可以表示为:
d(i,j) = d(i-1,j)+d(i,j-1)
这就叫做 每个阶段的状态或值可以通过上一阶段的状态或值推导出来 。
2.。 每个阶段的状态或值一旦决定,是不受后面阶段的状态或值影响的
因为题目说明,只能往右,下走,不能往左,上方向走。因此当前阶段是不会受到后面阶段的影响的。
3.。 是否有重复子问题的计算
d(i,j) = d(i-1,j)+d(i,j-1)
满足整个问题,没有特殊例子。所以满足。
4.。是否有边界
d(i,0)和 d(0,j)就是问题的边界。 另外 d(i,0)和 d(0,j)都等于1。
代码:
def uniqePaths(m, n):
cache = [ [1 for _ in range(m)] if j==0 else [1 if i==0 else None for i in range(m)] for j in range(n)]
for x in range(1, n): # 每一行就是一個階段,因為第一行已經知道結果了,所以從第二行開始
for y in range(1, m): # 每一個階段內部,因为第一列已经知道了,从第二列开始
cache[x][y] = cache[x-1][y] + cache[x][y-1]
return cache,cache[n-1][m-1]
def show(res):
cache,n = res
for i in cache:
print(i)
print('到达终点的路径总数有:{}'.format(n))
show(uniqePaths(4,5))
输出:
[二] 不同路径2,图中有障碍
图中有障碍,即有障碍那点不可到达。如:
思想:
1. 创建矩阵cache,记录某点的可到达路径数
2. 计算路径数时,用cache和输入矩阵grid比对,即,正常情况 cache[i][j] = cache[i][j-1]+cache[i-1][j],但如果 grid[i-1][j]==1,表示[i-1][j]处有障碍,所以cache[i][j] = cache[i][j-1]。同理,当grid[i][j-1]==1,则cache[i][j] = cache[i-1][j]。
class Solution(object):
def uniquePathsWithObstacles(self, grid): #grid是输入矩阵,矩阵中0表示没障碍,1表示有障碍
n = len(grid[0]) #列
m = len(grid) #行
if grid[0][0]==1:return 0 #若障碍点在入口,则直接返回0
cache = [[0]*n for j in range(m)] #初始化cache矩阵,cache用于记录 到达某点有多少种方法
'''边界初始化,因为动态规划肯定得有边界'''
cache[0][0] = 1
for i in range(1,m):
if grid[i][0]==1:cache[i][0]=0
else:cache[i][0] = cache[i-1][0]
for j in range(1,n):
if grid[0][j]==1:cache[0][j]=0
else:cache[0][j] = cache[0][j-1]
'''正式开始用动态规划计算某点的可到达路径数'''
for i in range(1,m):
for j in range(1,n):
if grid[i][j] == 1:
cache[i][j] = 0
continue
if grid[i-1][j]==1:
cache[i][j] = cache[i][j-1]
if grid[i][j-1]==1:
cache[i][j] = cache[i-1][j]
if grid[i][j-1]==0 and grid[i-1][j]==0:
cache[i][j] = cache[i][j-1]+cache[i-1][j]
print(cache) #[[1, 1, 1], [1, 0, 1], [1, 1, 2]]
return cache[-1][-1]
ss =Solution()
grid = [
[0,0,0],
[0,1,0],
[0,0,0]
]
res=ss.uniquePathsWithObstacles(grid)
print(res) #2
时间复杂度:O(mn),m、n表示输入矩阵的行数和列数
空间复杂度:O(mn),因为需要新建跟输入矩阵grid尺寸一样的矩阵cache
[三] 不同路径3,图中有障碍且图中所有格子都必须经过
思想:(回溯法)
创建一个字典(矩阵也可以)用于记录图中各个点是否已经经过。经过了为True,未经过为False。
从起始位置开始遍历,并修改每个遍历点的状态,如果到达结束位置,并且所有点的状态都为True,则是一条路径。遍历到结束位置后,回溯到上一个点,将该点状态重新修改为False,找新路径继续遍历。
class Solution:
def uniquePathsIII(self,grid):
from collections import defaultdict
start = None
end = None
visited_dict = defaultdict(bool)
row = len(grid)
col = len(grid[0])
for i in range(row):
for j in range(col):
if grid[i][j] == 1:
start = (i,j)
elif grid[i][j] == 2:
end = (i,j)
elif grid[i][j] == -1:
visited_dict[(i,j)] = True
elif grid[i][j] == 0:
visited_dict[(i, j)] = False
visited_dict[start] = True
self.counter = 0
self.backTrace(start,end,visited_dict,grid,row,col)
return self.counter
def backTrace(self,cur,end,visited_dict,grid,row,col):
if cur == end and all(visited_dict.values()):#结束回溯条件
self.counter+=1
return
for x,y in [[-1,0],[1,0],[0,1],[0,-1]]: #上、下、左、右四个方向
tmp_i = cur[0]+x
tmp_j = cur[1]+y
if 0<=tmp_i<row and 0<=tmp_j<col: #新坐标的边界判断
if grid[tmp_i][tmp_j] == -1: #如果到了障碍点,则选择其他方向
continue
else:
if visited_dict[(tmp_i,tmp_j)] == True: #若新坐标已经经过了,则下一个
continue
visited_dict[(tmp_i,tmp_j)] = True #走过的路标为True
#新坐标继续进行回溯
self.backTrace((tmp_i,tmp_j),end,visited_dict,grid,row,col)
visited_dict[(tmp_i,tmp_j)] = False
grid = [[1,0,0,0],[0,0,-1,0],[0,0,2,0]]
s = Solution()
res=s.uniquePathsIII(grid)
print(res) #1