在一个 $10^6\times 10^6$ 的网格中,每个网格上方格的坐标为 (x, y)
。
现在从源方格 source = [sx, sy]
开始出发,意图赶往目标方格 target = [$t_x$, $t_y$]
。数组 blocked
是封锁的方格列表,其中每个 blocked[i] = [xi, yi]
表示坐标为 (xi, yi)
的方格是禁止通行的。
每次移动,都可以走到网格中在四个方向上相邻的方格,只要该方格 不 在给出的封锁列表 blocked
上。同时,不允许走出网格。
只有在可以通过一系列的移动从源方格 source
到达目标方格 target
时才返回 true
。否则,返回 false
。
提示:
- 0 <=
blocked.length
<= 200 blocked[i].length
== 2- 0 <=
xi, yi
< 106 source.length
==target.length
== 2- 0 <=
sx, sy, tx, ty
< $10^6$ source
!=target
- 题目数据保证
source
和target
不在封锁列表内
一道dfs怎么都会超时的题目。最后得取巧。由于只有200个blocked
,所以最多只要经过20000格就能确保一定能走到外面。最后的极端情况见最后的例子。
class Solution: def isEscapePossible(self, blocked: List[List[int]], source: List[int], target: List[int]) -> bool: if blocked == []: return True blocked = set(map(tuple, blocked)) def dfs(self, x, y, target, state, blocked): if x == target[0] and y == target[1]: return True state.add((x,y)) if len(state) > 20000: return True px = [1,0,-1,0] py = [0,1,0,-1] for i in range(4): new_x = x + px[i] new_y = y + py[i] if new_x >= 0 and new_x < 10**6 and new_y >=0 and new_y < 10**6 and not (new_x,new_y) in state and not (new_x,new_y) in blocked: if dfs(self,new_x,new_y, target, state, blocked): return True return False return dfs(self, source[0], source[1], target, set(), blocked) and dfs(self, target[0], target[1], source, set(), blocked)
执行用时:1032 ms, 在所有 Python3 提交中击败了24.61%的用户
内存消耗:48.8 MB, 在所有 Python3 提交中击败了6.15%的用户
运行时间以及内存消耗较大。
极端例子:
[[0,199],[1,198],[2,197],[3,196],[4,195],[5,194],[6,193],[7,192],[8,191],[9,190],[10,189],[11,188],[12,187],[13,186],[14,185],[15,184],[16,183],[17,182],[18,181],[19,180],[20,179],[21,178],[22,177],[23,176],[24,175],[25,174],[26,173],[27,172],[28,171],[29,170],[30,169],[31,168],[32,167],[33,166],[34,165],[35,164],[36,163],[37,162],[38,161],[39,160],[40,159],[41,158],[42,157],[43,156],[44,155],[45,154],[46,153],[47,152],[48,151],[49,150],[50,149],[51,148],[52,147],[53,146],[54,145],[55,144],[56,143],[57,142],[58,141],[59,140],[60,139],[61,138],[62,137],[63,136],[64,135],[65,134],[66,133],[67,132],[68,131],[69,130],[70,129],[71,128],[72,127],[73,126],[74,125],[75,124],[76,123],[77,122],[78,121],[79,120],[80,119],[81,118],[82,117],[83,116],[84,115],[85,114],[86,113],[87,112],[88,111],[89,110],[90,109],[91,108],[92,107],[93,106],[94,105],[95,104],[96,103],[97,102],[98,101],[99,100],[100,99],[101,98],[102,97],[103,96],[104,95],[105,94],[106,93],[107,92],[108,91],[109,90],[110,89],[111,88],[112,87],[113,86],[114,85],[115,84],[116,83],[117,82],[118,81],[119,80],[120,79],[121,78],[122,77],[123,76],[124,75],[125,74],[126,73],[127,72],[128,71],[129,70],[130,69],[131,68],[132,67],[133,66],[134,65],[135,64],[136,63],[137,62],[138,61],[139,60],[140,59],[141,58],[142,57],[143,56],[144,55],[145,54],[146,53],[147,52],[148,51],[149,50],[150,49],[151,48],[152,47],[153,46],[154,45],[155,44],[156,43],[157,42],[158,41],[159,40],[160,39],[161,38],[162,37],[163,36],[164,35],[165,34],[166,33],[167,32],[168,31],[169,30],[170,29],[171,28],[172,27],[173,26],[174,25],[175,24],[176,23],[177,22],[178,21],[179,20],[180,19],[181,18],[182,17],[183,16],[184,15],[185,14],[186,13],[187,12],[188,11],[189,10],[190,9],[191,8],[192,7],[193,6],[194,5],[195,4],[196,3],[197,2],[198,1],[199,0]] [0,0] [200,200]
官方标答:
方法一:有限步数的广度优先
class Solution: def isEscapePossible(self, blocked: List[List[int]], source: List[int], target: List[int]) -> bool: """ BLOCKED: 在包围圈中 VALID: 不在包围圈中 FOUND: 无论在不在包围圈中,但在 n(n-1)/2 步搜索的过程中经过了 target """ BLOCKED, VALID, FOUND = -1, 0, 1 BOUNDARY = 10**6 if len(blocked) < 2: return True hash_blocked = set(tuple(pos) for pos in blocked) def check(start: List[int], finish: List[int]) -> int: sx, sy = start fx, fy = finish countdown = len(blocked) * (len(blocked) - 1) // 2 q = deque([(sx, sy)]) visited = set([(sx, sy)]) while q and countdown > 0: x, y = q.popleft() for nx, ny in [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]: if 0 <= nx < BOUNDARY and 0 <= ny < BOUNDARY and (nx, ny) not in hash_blocked and (nx, ny) not in visited: if (nx, ny) == (fx, fy): return FOUND countdown -= 1 q.append((nx, ny)) visited.add((nx, ny)) if countdown > 0: return BLOCKED return VALID if (result := check(source, target)) == FOUND: return True elif result == BLOCKED: return False else: result = check(target, source) if result == BLOCKED: return False return True
方法二:离散化 + 广度优先搜索
class Solution: def isEscapePossible(self, blocked: List[List[int]], source: List[int], target: List[int]) -> bool: if len(blocked) < 2: return True BOUNDARY = 10**6 # 离散化 rows = sorted(set(pos[0] for pos in blocked) | {source[0], target[0]}) columns = sorted(set(pos[1] for pos in blocked) | {source[1], target[1]}) r_mapping, c_mapping = dict(), dict() r_id = (0 if rows[0] == 0 else 1) r_mapping[rows[0]] = r_id for i in range(1, len(rows)): r_id += (1 if rows[i] == rows[i - 1] + 1 else 2) r_mapping[rows[i]] = r_id if rows[-1] != BOUNDARY - 1: r_id += 1 c_id = (0 if columns[0] == 0 else 1) c_mapping[columns[0]] = c_id for i in range(1, len(columns)): c_id += (1 if columns[i] == columns[i - 1] + 1 else 2) c_mapping[columns[i]] = c_id if columns[-1] != BOUNDARY - 1: c_id += 1 grid = [[0] * (c_id + 1) for _ in range(r_id + 1)] for x, y in blocked: grid[r_mapping[x]][c_mapping[y]] = 1 sx, sy = r_mapping[source[0]], c_mapping[source[1]] tx, ty = r_mapping[target[0]], c_mapping[target[1]] q = deque([(sx, sy)]) grid[sx][sy] = 1 while q: x, y = q.popleft() for nx, ny in [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]: if 0 <= nx <= r_id and 0 <= ny <= c_id and grid[nx][ny] != 1: if (nx, ny) == (tx, ty): return True q.append((nx, ny)) grid[nx][ny] = 1 return False
可以说两种方法都特别巧了。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/escape-a-large-maze
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
一些小知识:
- 二元list转set的操作一:
blocked = set(map(tuple, blocked))
- 二元list转set的操作二:
blocked = {tuple(p) for p in blocked}
- python 3.8更新 海象运算符 := 见 Python :=海象运算符最简单的解释
就是说从这里开始我就看不懂了