赞
踩
网格中内有不同的数字,数字表示某种特性,求解从起点到终点所需的最小值。
该类问题常用广度优先搜索方法BFS,优化可采用双向BFS,另外也可以尝试使用DP。
该类型题可以总结出以下模板:
def bfs(nums):
m = len(nums); n = len(nums[0])
visited = {(0, 0)} # 记录已访问的方格
q = [(0, 0, 1)] # 括号里面前两个数字表示已访问的方格的坐标,第三个数表示到达当前方格所需要是操作步数
direction = [(1, 0), (0, 1), (-1, 0), (0, -1)] # 搜索的方向
for a, b, dis in q:
if a == m - 1 and b == n - 1: return dis # 达到终点时返回dis
for i, j in direction:
a_new = a + i; b_new = b + j
if 0<= a_new < m and 0 <= b_new < n and nums[a_new][b_new] == 0: # 判断满足条件时,将满足的方格加入到visited和q中,同时dis需要进行加一操作
visited.add((a_new, b_new))
q.append((a_new, b_new, dis + 1))
return -1 # 以上for循环没有输出,说明得不到答案,执行到这里返回-1
另外一个版本:
def bfs(nums): m = len(nums); n = len(nums[0]) visited = {(0, 0)} # 记录已访问的方格 from collections import deque q = deque([(0, 0, 1)]) direction = [(1, 0), (0, 1), (-1, 0), (0, -1)] while q: for _ in range(len(q)): a, b, dis = q.popleft() if a == m - 1 and b == n - 1: return dis for i, j in direction: a_new = a + i; b_new = b + j if 0<= a_new < m and 0 <= b_new < n and nums[a_new][b_new] == 0: visited.add((a_new, b_new)) q.append((a_new, b_new, dis + 1)) return -1
# BFS class Solution: def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int: n = len(grid) if grid[0][0] or grid[n-1][n-1]: return -1 q = [(0, 0, 1)] grid[0][0] = 1 # 这里直接将grid进行修改,替换了visited集合 for i, j, d in q: if i == n-1 and j == n-1: return d for x, y in ((i-1,j-1),(i-1,j),(i-1,j+1),(i,j-1),(i,j+1),(i+1,j-1),(i+1,j),(i+1,j+1)): if 0 <= x < n and 0 <= y < n and not grid[x][y]: grid[x][y] = 1 q.append((x, y, d+1)) return -1 # BFS class Solution: def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int: n = len(grid) if n == 1: return 1 if grid[0][0] == 1 or grid[-1][-1] == 1: return -1 x = [-1, -1, -1, 0, 0, 1, 1, 1] y = [-1, 0, 1, -1, 1, -1, 0, 1] path = collections.deque([(0, 0, 1)]) while path: for _ in range(len(path)): a, b, dis = path.popleft() if a == n - 1 and b == n - 1: return dis for i, j in zip(x, y): new_a, new_b = a + i, b + j if 0 <= new_a < n and 0 <= new_b < n and grid[new_a][new_b] == 0: grid[new_a][new_b] = -1 path.append((new_a, new_b, dis + 1)) return -1
以下为双向BFS求解,代码不同的地方就是设计了三个集合,start、end和next_start,每一次的for循环找到的可行路径加入到next_start中,最后将其赋值给start,最后根据start与end的长短,将其进行调换。需要注意的是,这里需要在最内部的for循环中判断if (new_a, new_b) in end: return dis
# 双向BFS class Solution: def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int: n = len(grid) if n == 1: return 1 if grid[0][0] or grid[-1][-1]== 1: return -1 tuple_offset = ((1, 0), (0, 1), (1, 1), (-1, 0), (0, -1), (1, -1), (-1, 1), (-1, -1)) start = {(0, 0)} end = {(n - 1, n - 1)} dis = 1 while start: dis += 1 next_start = set() for x, y in start: for x_offset, y_offset in tuple_offset: new_a, new_b = x + x_offset, y + y_offset # 两端碰撞 if (new_a, new_b) in end: return dis if 0 <= new_a < n and 0 <= new_b < n and grid[new_a][new_b] == 0: grid[new_a][new_b] = -1 next_start.add((new_a, new_b)) start = next_start if len(end) < len(start): start, end = end, start return -1
该题也可以采用动态规划的思路进行求解。
DP 解题思路(已通过所有测试样例) python3
import copy class Solution: def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int: # 逻辑是 dp[i][j] = arround_min(i,j) + 1 dx = [-1, -1, -1, 0, 0, 1, 1, 1] dy = [-1, 0, 1, 1, -1, -1, 0, 1] dist = 0 N = len(grid) # 边界条件 if grid[0][0] == 1 or grid[-1][-1] == 1:return -1 if N == 1 :return 1 # 将不可走路径设为inf for i in range(N): for j in range(N): if grid[i][j] == 0: grid[i][j] = inf else: grid[i][j] = 'stop' grid[0][0] = 1 # 定义一个arround_min函数 def arround_min(i, j): _min = inf for k in range(8): x, y = i + dx[k], j + dy[k] if (0 <= x < N and 0 <= y < N and grid[x][y] != 'stop'): _min = min(_min, grid[x][y]) return _min #这里必须深拷贝,因为是二维数组 gridnew = copy.deepcopy(grid) while(True): #终止条件 if grid[-1][-1] != inf: return grid[-1][-1] #循环 for j in range(N): for i in range(N): if i == 0 and j == 0: continue if grid[i][j] != 'stop': grid[i][j] = arround_min(i, j) + 1 # 判断grid是否还有变化,若没有则证明无解 if gridnew == grid: return -1 gridnew = copy.deepcopy(grid)
注意:第一、二条规则是指蛇可以进行平移(当不存在障碍物的时候)
该题与上一题不同的是,本题没有特定的dircetion,需要根据周围障碍物的情况而进行方向的选择,因此需要进行枚举,代码如下:
class Solution: def minimumMoves(self, grid: List[List[int]]) -> int: n = len(grid) queue = [(0, 0, 0, 1, 0)] visit = {(0, 0, 0, 1)} for a1, b1, a2, b2, step in queue: if (a1, b1, a2, b2) == (n - 1, n - 2, n - 1, n -1): return step if a1 + 1 < n and a2 + 1 < n and grid[a1 + 1][b1] == grid[a2 + 1][b2] == 0: if (a1 + 1, b1, a2 + 1, b2) not in visit: visit.add((a1 + 1, b1, a2 + 1, b2)) queue.append((a1 + 1, b1, a2 + 1, b2, step + 1)) if a1 == a2 and (a1, b1, a1 + 1, b1) not in visit: visit.add((a1, b1, a1 + 1, b1)) queue.append((a1, b1, a1 + 1, b1, step + 1)) if b1 + 1 < n and b2 + 1 < n and grid[a1][b1 + 1] == grid[a2][b2 + 1] == 0: if (a1, b1 + 1, a2, b2 + 1) not in visit: visit.add((a1, b1 + 1, a2, b2 + 1)) queue.append((a1, b1 + 1, a2, b2 + 1, step + 1)) if b1 == b2 and (a1, b1, a1, b2 + 1) not in visit: visit.add((a1, b1, a1, b2 + 1)) queue.append((a1, b1, a1, b2 + 1, step + 1)) return -1
该题由于可以对障碍物进行清除,因此不能在原有的数组中进行修改,即需要设计一个visited数组已表示已访问的网格。
class Solution: def shortestPath(self, grid: List[List[int]], k: int) -> int: m, n = len(grid), len(grid[0]) if k >= m + n - 3: return m + n - 2 q = [(0, 0, 0, 0)] visited = {(0, 0, 0)} direct = [(1, 0), (0, 1), (-1, 0), (0, -1)] for i, j, w, step in q: if step > k: continue if i == m - 1 and j == n - 1: return w for a, b in direct: i_new = i + a j_new = j + b if 0 <= i_new < m and 0<= j_new < n: step_new = step + 1 if grid[i_new][j_new] == 1 else step if (i_new, j_new, step_new) not in visited: visited.add((i_new, j_new, step_new)) q.append((i_new, j_new, w + 1, step_new)) return -1
在迷宫中有一个球,里面有空的空间和墙壁。球可以通过滚上,下,左或右移动,
但它不会停止滚动直到撞到墙上。当球停止时,它可以选择下一个方向。
给定球的起始位置,目的地和迷宫,确定球是否可以停在终点。
迷宫由二维数组表示。1表示墙和0表示空的空间。你可以假设迷宫的边界都是墙。开始和目标坐标用行和列索引表示。
例:
输入:
map =
[
[0,0,1,0,0],
[0,0,0,0,0],
[0,0,0,1,0],
[1,1,0,1,1],
[0,0,0,0,0]
]
start = [0,4]
end = [3,2]
输出:
false
def bfs(nums, start, end): m = len(nums) n = len(nums[0]) if not m or not n: return True visited = {(start[0], start[1])} q = [(start[0], start[1])] direction = [(1, 0), (0, 1), (-1, 0), (0, -1)] for x, y in q: # for a, b in qe: if x == end[0] and y == end[1]: return True for i, j in direction: a = x; b = y # 注意此处需要复制一份a、b,确保每一次while循环a、b的值都能为(x, y)!!!! while 0 <= a < m and 0 <= b < n and nums[a][b] == 0: a += i b += j a -= i b -= j if (a, b) not in visited: visited.add((a, b)) q.append((a, b)) return False map = [[0,0,1,0,0], [0,0,0,0,0], [0,0,0,1,0], [1,1,0,1,1], [0,0,0,0,0]] start = [0, 4] end = [4, 4] a = bfs(map, start, end) print(a)
在迷宫中有一个球,里面有空的空间和墙壁。球可以通过滚上,下,左或右移动,但它不会停止滚动直到撞到墙上。当球停止时,它可以选择下一个方向。
给定球的起始位置,目标和迷宫,找到最短距离的球在终点停留。距离是由球从起始位置(被排除)到目的地(包括)所走过的空空间的数量来定义的。如果球不能停在目的地,返回-1。
迷宫由二维数组表示。1表示墙和0表示空的空间。你可以假设迷宫的边界都是墙。开始和目标坐标用行和列索引表示。
Example 1: Input: (rowStart, colStart) = (0,4) (rowDest, colDest)= (4,4) 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 Output: 12 Explanation: (0,4)->(0,3)->(1,3)->(1,2)->(1,1)->(1,0)->(2,0)->(2,1)->(2,2)->(3,2)->(4,2)->(4,3)->(4,4) Example 2: Input: (rowStart, colStart) = (0,4) (rowDest, colDest)= (0,0) 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 Output: 6 Explanation: (0,4)->(0,3)->(1,3)->(1,2)->(1,1)->(1,0)->(0,0)
这一题与上一题的代码基本一样,只是需要一个额外的变量记录所走的路程,因此将该变量加入到q列表中。代码如下:
def bfs(nums, start, end): m = len(nums) n = len(nums[0]) if not m or not n: return True visited = {(start[0], start[1])} q = [(start[0], start[1], 0)] direction = [(1, 0), (0, 1), (-1, 0), (0, -1)] for x, y, dis in q: # for a, b in qe: if x == end[0] and y == end[1]: return dis for i, j in direction: a = x; b = y; d = dis# 注意此处需要复制一份a、b,确保每一次while循环的a、b并值都能为(x, y)!!!! while 0 <= a < m and 0 <= b < n and nums[a][b] == 0: a += i b += j d += 1 a -= i b -= j d -= 1 if (a, b) not in visited: visited.add((a, b)) q.append((a, b, d)) return -1 map = [[0,0,1,0,0], [0,0,0,0,0], [0,0,0,1,0], [1,1,0,1,1], [0,0,0,0,0]] start = [0, 4] end = [4, 4] a = bfs(map, start, end) print(a)
以上题均只是计算最后的结果,如何输出路径?在一些笔试中,常会对路径进行限制,(如一次华为的笔试,在连续的直线路径上,可以一下跳跃两步等等类似的情况)这时候就可以先求出路径,然后再对所选择的路径再进行处理(目前我想的是这样处理,有没有一种方法,在寻路径的过程就进行计算,本人还没有想到,希望后续能够学习到)
这里就先放别人的实现,参考链接。但是这种目前用在采用动态规划方法中比较好理解,但是如果题目是如上几种,采用的是BFS,该路径如何打印?本人也一直很无解-_-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。