题目描述
思路分析
提示1 怎么判断两者是否可以互通?
本质上是判断阻碍方块有没有把其中一方圈起来。具体来说:
- 将s圈起来,且从s出发无法遍历到t
- 将t圈起来,且从t出发无法遍历到s 那么s和t将永远无法互通。
提示2 怎么判断一个点是否被围起来了呢?
因为阻碍方块数是有限的,所以这些方块能围成的最大面积S也是有限的。
比如我们从s出发遍历,发现能遍历的位置数量比S更大,说明s没有被围住;反之s被围住了。
提示3 怎么判断这个S的大小呢?
你会下围棋吗? 如果你入门过围棋,那么你一定听说过"金角银边草肚皮". 如果懂这个道理,那么你一定能很快反应过来,阻碍方块可以围成的最大面积是如何计算的.
假设阻碍方块有n个,那么这n个方块作为一条直线围在角上,会得到最大的面积,n*(n-1)//2.
提示4 我不算这个S行不行?
比如我没有想到要去计算可遍历的面积来判断是否被围困,还有没有别的思路?
有的。假如你从s出发,最新一轮的遍历队列长度大于num_block且从t出发,最新一轮的遍历队列长度大于num_block,那么说明s和t都没被困住。一样能通过此题。
其余情况,就像正常的双向广搜处理即可。 比如
- 从s出发,判断新的位置是否可以加入s_vis. t同理.
- s_q弹出新的位置时,看看新的位置是否出现在t_vis中. t_q同理.
代码
class Solution:
def isEscapePossible(self, blocked: List[List[int]], source: List[int], target: List[int]) -> bool:
MAX_LEN = 10 ** 6
blocked = set(tuple(b) for b in blocked)
num_block = len(blocked)
s_q = deque([source])
s_vis = set([tuple(source)])
s_cnt = 1
t_q = deque([target])
t_vis = set([tuple(target)])
t_cnt = 1
dx = [0,0,1,-1]
dy = [1,-1,0,0]
while s_q and t_q :
# if len(s_q) > num_block and len(t_q) > num_block :
# return True
if s_cnt > num_block * (num_block - 1) // 2 and t_cnt > num_block * (num_block - 1) // 2 :
return True
s_len = len(s_q)
for _ in range(s_len):
cur_x , cur_y = s_q.popleft()
if (cur_x,cur_y) in t_vis :
return True
for i in range(4) :
nx , ny = cur_x + dx[i] , cur_y + dy[i]
if nx >= 0 and nx < MAX_LEN and ny >= 0 and ny < MAX_LEN :
if (nx,ny) not in blocked and (nx,ny) not in s_vis :
s_q.append((nx,ny))
s_vis.add((nx,ny))
s_cnt += 1
t_len = len(t_q)
for _ in range(t_len):
cur_x , cur_y = t_q.popleft()
if (cur_x,cur_y) in s_vis :
return True
for i in range(4) :
nx , ny = cur_x + dx[i] , cur_y + dy[i]
if nx >= 0 and nx < MAX_LEN and ny >= 0 and ny < MAX_LEN :
if (nx,ny) not in blocked and (nx,ny) not in t_vis :
t_q.append((nx,ny))
t_vis.add((nx,ny))
t_cnt += 1
return False