leetcode day 1 【1970. 你能穿过矩阵的最后一天】 二分查找+BFS/DFS

本文介绍了作者三次尝试解决岛屿穿越问题的过程,从二分查找结合BFS到去除冗余并改进到DFS。最终通过DFS实现的时间复杂度降低至O(nmlogn),并探讨了问题的图论本质和可能的扩展议题,如最短步数求解和路径探索。
摘要由CSDN通过智能技术生成

在这里插入图片描述

  • 第一次尝试思路:二分查找 + 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到,则结束,根据前驱矩阵可以找到一条通路和并统计步数,该路径一定为最短路径之一。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值