- 第一次尝试思路:二分查找 + BFS
记录所有第一行是陆地(0)的元素,入队列。然后对每一个元素,去尝试BFS,看是否能到达底端。
可以跑通也是对的,但超出时间限制。时间复杂度感觉是o(n^2mlogn)。并且代码很冗长,多此一举的flag(判断是否被找过)和两个队列(两个循环)【会重复对某些格子搜索】。
class Solution:
def latestDayToCross(self, row: int, col: int, cells: List[List[int]]) -> int:
day_num = len(cells)
start_day = 0
end_day = day_num -1
while(start_day <= end_day):
medium_day = (start_day + end_day) // 2
Find = False
matrix = [[0] * col for _ in range(row)]
for cell in cells[0:(medium_day+1)]:
matrix[cell[0]-1][cell[1]-1] = 1
star_points = deque()
for i in range(col):
if matrix[0][i] == 0:
star_points.append([0, i])
while(len(star_points)):
flag = [[0] * col for _ in range(row)]
find_points = deque()
point_x, point_y = star_points.popleft()
flag[point_x][point_y] = 1
find_points.append((point_x, point_y))
while(len(find_points)):
point_x, point_y = find_points.popleft()
for x,y in [(point_x-1, point_y),(point_x,point_y-1),(point_x+1,point_y),(point_x,point_y+1)]:
if 0<= x < row and 0<= y < col:
if matrix[x][y] == 0 and flag[x][y] == 0:
if x == row -1:
Find = True
break
flag[x][y] = 1
find_points.append((x,y))
if Find:
start_day = medium_day + 1
else:
end_day = medium_day -1
return start_day
- 第二次除去冗余后:
用一个循环,凡是被找过的就直接对matrix置1(水域)【因为不会重复对格子搜索】
依旧超出时间限制,但代码看起来简洁许多
class Solution:
def latestDayToCross(self, row: int, col: int, cells: List[List[int]]) -> int:
day_num = len(cells)
start_day = 0
end_day = day_num -1
while(start_day <= end_day):
medium_day = (start_day + end_day) // 2
Find = False
matrix = [[0] * col for _ in range(row)]
for cell in cells[0:(medium_day+1)]:
matrix[cell[0]-1][cell[1]-1] = 1
find_points = deque()
for i in range(col):
if matrix[0][i] == 0:
matrix[0][i] = 1
find_points.append([0, i])
while(len(find_points)):
point_x, point_y = find_points.popleft()
for x,y in [(point_x-1, point_y),(point_x,point_y-1),(point_x+1,point_y),(point_x,point_y+1)]:
if 0<= x < row and 0<= y < col and matrix[x][y] == 0 :
if x== row -1:
Find = True
break
matrix[point_x][point_y] = 1
find_points.append((x,y))
if Find :
start_day = medium_day + 1
else:
end_day = medium_day -1
return start_day
- 第三次尝试(AC): 时间复杂度减小为o(nmlogn)
fix第二次应该在搜索到岛屿(0)将其入队列的时候立马置1,而不是弹出队列去搜索四周时才置1,否则该点可能被多次送入队列,导致重复搜索。
class Solution:
def latestDayToCross(self, row: int, col: int, cells: List[List[int]]) -> int:
day_num = len(cells)
start_day = 0
end_day = day_num -1
while(start_day <= end_day):
medium_day = (start_day + end_day) // 2
Find = False
matrix = [[0] * col for _ in range(row)]
for cell in cells[0:(medium_day+1)]:
matrix[cell[0]-1][cell[1]-1] = 1
find_points = deque()
for i in range(col):
if matrix[0][i] == 0:
matrix[0][i] = 1
find_points.append([0, i])
while(len(find_points)):
point_x, point_y = find_points.popleft()
for x,y in [(point_x-1, point_y),(point_x,point_y-1),(point_x+1,point_y),(point_x,point_y+1)]:
if 0<= x < row and 0<= y < col and matrix[x][y] == 0 :
if x== row -1:
Find = True
break
matrix[x][y] = 1
find_points.append((x,y))
if Find :
start_day = medium_day + 1
else:
end_day = medium_day -1
return start_day
- 第四次尝试(AC): 二分查找+DFS
class Solution:
def latestDayToCross(self, row: int, col: int, cells: List[List[int]]) -> int:
self.row, self.col = row, col
day_num = len(cells)
start_day = 0
end_day = day_num -1
while(start_day <= end_day):
medium_day = (start_day + end_day) // 2
self.Find = False
matrix = [[0] * col for _ in range(row)]
self.matrix = matrix
for cell in cells[0:(medium_day+1)]:
matrix[cell[0]-1][cell[1]-1] = 1
find_points = deque()
for i in range(col):
if matrix[0][i] == 0:
find_points.append([0, i])
while(len(find_points)):
if self.Find:
break
point_x, point_y = find_points.popleft()
self.DFS(point_x, point_y)
if self.Find :
start_day = medium_day + 1
else:
end_day = medium_day -1
return start_day
def DFS(self, x: int, y: int):
if self.matrix[x][y] == 0:
if x == self.row -1:
self.Find = True
return
self.matrix[x][y] = 1
for nx , ny in [(x-1,y), (x,y-1), (x+1,y), (x,y+1)]:
if self.Find:
return
if 0<= nx < self.row and 0<= ny < self.col:
self.DFS(nx, ny)
return
总结
这实际上是一个图,图的出度为四周的岛屿,是一个密集边图。水可能把整片区域分成了若干块岛屿,也就是若干个连通分量。
1. 二分查找的结构:
star = 0
end = lenth -1
while(start <= end):
medi = (start + end) // 2
if table[media] == value :
return True
elif table[media] > value :
end = media - 1
elif table[media] < value :
start = media + 1
return False
2. python中队列的使用:
q = deque()
q.append(data)
q.popleft()
lenth = len(q)
3. 题目扩展:
这道题可以往以下几个方面扩展
- 1.求原题中移动的步数
① 如果用DFS(第四次尝试),那么可以用一个变量count记录递归次数,每次递归则count+1,回溯则count-1。当找到通路时的count值即为步数。(当然由于可能具有多条路径,所以该步数不一定为最短步数)
②如果用BFS,那么参考扩展3,并且该步数一定为最短步数。
- 2.题目不是问“可达的最后一天”,而是问“最短步数为K步的最后一天”
因为越到后面随着淹没的岛屿越多,可选择的路径会越少,步数只会增不会减,所以这和可达本质上一样,都是一个临界问题,可以用二分查找解决,查找的对象为最短步数。某天的最短步数求法:对于第一行中的每一个岛屿,都执行一次扩展1中的BFS求最短步数,选取最小的那个最短步数作为当天的最短步数。
- 3.求任意两点是否可达
实际上就是图的遍历,这可以对某点(A)进行BFS或者DFS,遍历该连通图上的所有区域,如果搜索不到另一点(B),则代表两点不互通。
- 4.若两点可达,求最短路径和步数
实际上就是图中的最短路径算法,只不过权值全为1的图具有特殊性,最短路径算法可以退化为BFS。
具体做法为:BFS时用一个队列维护要broadcast的节点,每次都往四个方向broadcast,一旦入队列则对矩阵中该点置零代表已经被pick。同时用一个额外的前驱矩阵记录该点是被谁broadcast到的。一旦B点被broadcast到,则结束,根据前驱矩阵可以找到一条通路和并统计步数,该路径一定为最短路径之一。