一.问题描述
In a 1 million by 1 million grid, the coordinates of each grid square are (x, y)
with 0 <= x, y < 10^6
.
We start at the source
square and want to reach the target
square. Each move, we can walk to a 4-directionally adjacent square in the grid that isn't in the given list of blocked
squares.
Return true
if and only if it is possible to reach the target square through a sequence of moves.
Example 1:
Input: blocked = [[0,1],[1,0]], source = [0,0], target = [0,2]
Output: false
Explanation:
The target square is inaccessible starting from the source square, because we can't walk outside the grid.
Example 2:
Input: blocked = [], source = [0,0], target = [999999,999999]
Output: true
Explanation:
Because there are no blocked cells, it's possible to reach the target square.
Note:
0 <= blocked.length <= 200
blocked[i].length == 2
0 <= blocked[i][j] < 10^6
source.length == target.length == 2
0 <= source[i][j], target[i][j] < 10^6
source != target
二.解题思路
这道题首先想到的是深度优先搜索,即以一个点开始,往上下左右四个方向扩散找路。但是我们需要注意一下整个迷宫的空间范围,可以看到maze的空间范围是10^6*10^6,感觉不做任何处理这样子相当于要遍历整个空间,会惨遭tle,最后实现之后果不其然,惨遭tle。
注意到blocked的长度最大只有200,比10^6小的多,重心应该在blocked上
首先我们先明确一点,如果被挡住的区域是x格,我从source点往四处扩散,用一个step变量记录我的步数,每走到一个格我就+一步,如果我走了x+1步,那么这个source点定然没有被围住出不去。
因为如果source点被围住出不去了了,那么blocked的所有点可以和边界围成一个闭区域,假设这个闭区域的空间为x,那么从source点往外遍历最多只能走x步,如果我遍历能走到x+1步,说明这个区域没有被围住,有地方可以出去
然后我们再对target也来一次这样的遍历,如果source和target都没有被围住,那么两者能通
最大的迭代次数就是围住区域的最大值乘2
而我们这道题重点就在最大的围起来的闭区域会是多少
很多人会认为是长和宽相等的时候围住的区域最大,这在连续的问题上是这样的
但是我们的空间是离散的
答案是B*(B-1)/2,B是blocked的长度
最大的围住的区域是当blocked以45度的方向排成一个斜线和边界线构成一个闭区域的时候
证明:
当两个cell水平排列的时候,我们可以把一个cell往上或往下下移一个,这样子还是一个闭区域,但是多了一个格子的空间
当两个cell垂直排列的时候,我们可以把一个cell往左或往右移一个,这样子还是一个闭区域,但是多了一个格子的空间
同时要注意新一个变量来跟踪已经遍历过的地方,不然会死循环
还有把blocked最好转变成set,这样查询一个变量是否在set里面的时候是O(1)时间复杂度
之后看到其他高效算法会更新
更新:
也可以不用围住的区域大小,用level来做剪枝的判断,不过这样用在bfs上才行,dfs还是围住的区域才行
level指的是第几条对角线
level=1 指的是[0,0]
level2 是[1,0]和[0,1]连成的对角线,因为每次向下或者向上扩散一个level,所以最大step是len(blocked)*2
更多leetcode算法题解法请关注我的专栏leetcode算法从零到结束或关注我
欢迎大家一起套路一起刷题一起ac
三.源码
class Solution:
def isEscapePossible(self, blocked: List[List[int]], source: List[int], target: List[int]) -> bool:
bkset=set(map(tuple,blocked))
flag=set()
return self.findtarget(bkset,flag,tuple(source),tuple(target),0) and \
self.findtarget(bkset,flag,tuple(target),tuple(source),0)
def findtarget(self,bkset,flag,source,target,step):
if step>=20000 or source==target:
return True
if source[0]<0 or source[0]>=1000000 or source[1]<0 or source[1]>=1000000 or source in flag or source in bkset:
return False
flag.add(source)
return self.findtarget(bkset,flag,(source[0]+1,source[1]),target,step+1) or \
self.findtarget(bkset,flag,(source[0]-1,source[1]),target,step+1) or \
self.findtarget(bkset,flag,(source[0],source[1]+1),target,step+1) or \
self.findtarget(bkset,flag,(source[0],source[1]-1),target,step+1)