备战菊厂笔试2-BFS记忆化MLE?用Set去重-Set会TLE?用SortedSet剪枝

200.岛屿数量

很明显的DFS连通性判断

遍历地图找1,然后开始传染(如果不想修改原本数据集可以用vis存储已访问数据)

python 复制代码
class Solution:
    def numIslands(self, grid):

        lx=len(grid)
        ly=len(grid[0])
        d=[(0,1),(0,-1),(1,0),(-1,0)]
        
        def get_nei(x,y):
            neis=[]
            for dx,dy in d:
                nx,ny=x+dx,y+dy
                if 0<=nx<lx and 0<=ny<ly:
                    neis.append((nx,ny))#tuple方便解包

            return neis
        
        def dfs(x,y):
            if grid[x][y]=='1':
                grid[x][y]='0'
                for nx,ny in get_nei(x,y):
                    dfs(nx,ny)#传染0
        ans=0

        for i in range(lx):
            for j in range(ly):
                if grid[i][j]=='1':
                    dfs(i,j)
                    ans+=1
        return ans

if __name__=='__main__':
    grid = [
    ["1","1","0","0","0"],
    ["1","1","0","0","0"],
    ["0","0","1","0","0"],
    ["0","0","0","1","1"]
    ]

    sol=Solution()#创建对象
    ans=sol.numIslands(grid)
    print(ans)

但是这样只击败35%的解法

不用getnei,直接在dfs判断,去掉解包

去掉getnei,去掉解包(解包的在数据很多时会影响时间复杂度)

python 复制代码
def numIslands(self, grid):
    if not grid:
        return 0
        
    rows, cols = len(grid), len(grid[0])
    count = 0
    
    def dfs(r, c):
        # 边界条件或已访问过(水域)
        if r < 0 or c < 0 or r >= rows or c >= cols or grid[r][c] == '0':
            return
        
        # 标记为已访问
        grid[r][c] = '0'
        
        # 直接检查四个方向
        dfs(r+1, c)
        dfs(r-1, c)
        dfs(r, c+1)
        dfs(r, c-1)
    
    for i in range(rows):
        for j in range(cols):
            if grid[i][j] == '1':
                count += 1
                dfs(i, j)
                
    return count

如果害怕栈溢出那么可以用bfs

python 复制代码
from collections import deque

def numIslands(self, grid):
    if not grid:
        return 0
        
    rows, cols = len(grid), len(grid[0])
    count = 0
    
    for i in range(rows):
        for j in range(cols):
            if grid[i][j] == '1':
                count += 1
                grid[i][j] = '0'  # 标记为已访问
                
                # BFS
                queue = deque([(i, j)])
                while queue:
                    r, c = queue.popleft()
                    directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
                    
                    for dr, dc in directions:
                        nr, nc = r + dr, c + dc
                        if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == '1':
                            queue.append((nr, nc))
                            grid[nr][nc] = '0'  # 标记为已访问
    
    return count

2617.网格图中最少访问的格子数

2617. 网格图中最少访问的格子数

分析题目,对于x,y点可以向右或者向下,其行动能力为该节点的权值

我刚开始还想着用DFS表示着来做,但是这复杂度也太高了!

得利用BFS逐层拓展的性质,第一次碰到就是最短

得vis记忆化:之前层走的肯定是先到的

python 复制代码
from collections import deque

class Solution:
    def minimumVisitedCells(self, grid):
        if grid == [[0]]:
            return 1

        lx = len(grid)
        ly = len(grid[0])

        visited = [[False] * ly for _ in range(lx)]
        queue = deque()
        queue.append((0, 0, 1))  # (x, y, 当前步数)

        while queue:
            x, y, step = queue.popleft()

            if visited[x][y]:
                continue
            visited[x][y] = True

            # 尝试向下跳
            for i in range(1, grid[x][y] + 1):
                nx = x + i
                if nx >= lx:
                    break
                if not visited[nx][y]:
                    if nx == lx - 1 and y == ly - 1:
                        return step + 1
                    queue.append((nx, y, step + 1))

            # 尝试向右跳
            for i in range(1, grid[x][y] + 1):
                ny = y + i
                if ny >= ly:
                    break
                if not visited[x][ny]:
                    if x == lx - 1 and ny == ly - 1:
                        return step + 1
                    queue.append((x, ny, step + 1))

        return -1

注意:得在类外面导入库!

注意特判!

起点就是终点的时候不是输出-1,而是输出1

python 复制代码
        if grid==[[0]]:
            return 1

结果后面MLE了

(这道题不用dijkstra,因为每步代价是一样的)

MLE主要是因为vis占用的内存过大

用set去重

用两个二维数组,一个存储每一行未被访问的列,另一个存储每一列未被访问的行

那么就从原来的加入到 vis 变成从 set remove

python 复制代码
from collections import deque

class Solution:
    def minimumVisitedCells(self, grid):
        if grid == [[0]]:
            return 1

        lx = len(grid)
        ly = len(grid[0])

        #每一行未被访问的列
        lie=[set(range(ly)) for i in range(lx)]
        hang=[set(range(lx)) for i in range(ly)]
        #每一列未被访问的行

        lie[0].remove(0)
        hang[0].remove(0)
        queue=deque()
        queue.append((0,0,1))

        while queue:
            x,y,step=queue.popleft()

            for i in range(1,grid[x][y]+1):
                nx=x+i
                if nx>=lx:
                    break
                if nx in hang[y]:
                    if nx==lx-1 and y==ly-1:
                        return step+1
                    queue.append((nx,y,step+1))
                    hang[y].remove(nx)

            for i in range(1,grid[x][y]+1):
                ny=y+i
                if ny>=ly:
                    break
                if ny in lie[x]:
                    if x==lx-1 and ny==ly-1:
                        return step+1
                    queue.append((x,ny,step+1))
                    lie[x].remove(ny)
        return -1

但是这样TLE了

用SortedSet有序剪枝

什么是SortedSet?

和set类似,但会保持元素始终按顺序排列 ,支持范围查询和有序操作,非常适合搜索剪枝优化、模拟平衡树等

基本性质

和set一样不允许重复元素

元素自动排序(默认升序)

支持快速:

插入和删除

查找、区间查询、二分查找

导入
python 复制代码
from sortedcontainers import SortedSet  # 第三方库,需pip安装
常用操作
初始化
python 复制代码
ss=SortedSet([1,9,2,8])

ss=SortedSet(列表名)
添加与删除
python 复制代码
s.add( 元素值 )

s.discard( 元素值 )
索引(因为有序的所以支持bisect,类似于list)
python 复制代码
print(s[0])     # 输出最小值(1)
print(s[-1])    # 输出最大值(9)
print(s.bisect_left(5))  # 2,表示第一个 ≥5 的位置
print(s.bisect_right(5)) # 3,表示第一个 >5 的位置
范围查询 irange( , ) !!!
python 复制代码
# 获取大于等于 2 且小于等于 7 的所有元素(闭区间)
print(list(s.irange(2, 7)))  # [5, 7]
# 获取大于 3 的元素(开区间)
print(list(s.irange(3, 9, inclusive=(False, True)))  # [5, 7, 9]

所以这题里面我们可以用 irange快速找到可以跳远的位置

我们可以用 list + bisect 实现类似SortedSet

就是要注意列表的查重

本题题解

python 复制代码
from collections import deque
from bisect import bisect_right
from sortedcontainers import SortedSet  # 第三方库,需安装

class Solution:
    def minimumVisitedCells(self, grid):
        if grid == [[0]]:
            return 1

        m, n = len(grid), len(grid[0])
        row = [SortedSet(range(n)) for _ in range(m)]
        col = [SortedSet(range(m)) for _ in range(n)]

        queue = deque([(0, 0, 1)])
        row[0].discard(0)
        col[0].discard(0)

        while queue:
            x, y, step = queue.popleft()

            max_jump = grid[x][y]

            # 向右推进
            candidates = list(row[x].irange(y + 1, y + max_jump))
            for ny in candidates:
                if x == m - 1 and ny == n - 1:
                    return step + 1
                queue.append((x, ny, step + 1))
                row[x].discard(ny)

            # 向下推进
            candidates = list(col[y].irange(x + 1, x + max_jump))
            for nx in candidates:
                if nx == m - 1 and y == n - 1:
                    return step + 1
                queue.append((nx, y, step + 1))
                col[y].discard(nx)

        return -1

官方题解

和Dijkstra一样都贪心先处理小的-最小堆

python 复制代码
class Solution:
    def minimumVisitedCells(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        dist = [[-1] * n for _ in range(m)]
        dist[0][0] = 1
        row, col = [[] for _ in range(m)], [[] for _ in range(n)]

        def update(x: int, y: int) -> int:
            return y if x == -1 or y < x else x
        
        for i in range(m):
            for j in range(n):
                while row[i] and row[i][0][1] + grid[i][row[i][0][1]] < j:
                    heapq.heappop(row[i])
                if row[i]:
                    dist[i][j] = update(dist[i][j], dist[i][row[i][0][1]] + 1)

                while col[j] and col[j][0][1] + grid[col[j][0][1]][j] < i:
                    heapq.heappop(col[j])
                if col[j]:
                    dist[i][j] = update(dist[i][j], dist[col[j][0][1]][j] + 1)

                if dist[i][j] != -1:
                    heapq.heappush(row[i], (dist[i][j], j))
                    heapq.heappush(col[j], (dist[i][j], i))

        return dist[m - 1][n - 1]

单调栈优化DP

python 复制代码
class Solution:
    def minimumVisitedCells(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        col_stacks = [[] for _ in range(n)]  # 每列的单调栈
        for i in range(m - 1, -1, -1):
            row_st = []  # 当前行的单调栈
            for j in range(n - 1, -1, -1):
                g = grid[i][j]
                col_st = col_stacks[j]
                mn = inf if i < m - 1 or j < n - 1 else 1
                if g:  # 可以向右/向下跳
                    # 在单调栈上二分查找最优转移来源
                    k = bisect_left(row_st, -(j + g), key=lambda p: p[1])
                    if k < len(row_st):
                        mn = row_st[k][0] + 1
                    k = bisect_left(col_st, -(i + g), key=lambda p: p[1])
                    if k < len(col_st):
                        mn = min(mn, col_st[k][0] + 1)
                if mn < inf:
                    # 插入单调栈
                    while row_st and mn <= row_st[-1][0]:
                        row_st.pop()
                    row_st.append((mn, -j))  # 保证下标单调递增,方便调用 bisect_left
                    while col_st and mn <= col_st[-1][0]:
                        col_st.pop()
                    col_st.append((mn, -i))  # 保证下标单调递增,方便调用 bisect_left
        return mn if mn < inf else -1  # 最后一个算出的 mn 就是 f[0][0]

线段树

区间查询+单点更新

python 复制代码
import sys
sys.setrecursionlimit(1 << 25)

INF = float('inf')

class SegmentTree:
    def __init__(self, size):
        self.N = size
        self.tree = [INF] * (4 * size)

    def update(self, o, l, r, idx, val):
        if l == r:
            self.tree[o] = val
            return
        m = (l + r) // 2
        if idx <= m:
            self.update(o * 2, l, m, idx, val)
        else:
            self.update(o * 2 + 1, m + 1, r, idx, val)
        self.tree[o] = min(self.tree[o * 2], self.tree[o * 2 + 1])

    def query(self, o, l, r, L, R):
        if L > R:
            return INF
        if L <= l and r <= R:
            return self.tree[o]
        m = (l + r) // 2
        res = INF
        if L <= m:
            res = min(res, self.query(o * 2, l, m, L, R))
        if R > m:
            res = min(res, self.query(o * 2 + 1, m + 1, r, L, R))
        return res

class Solution:
    def minimumVisitedCells(self, grid):
        m, n = len(grid), len(grid[0])
        minl = [SegmentTree(m) for _ in range(n)]  # 每一列的线段树
        ans = INF

        for i in reversed(range(m)):
            minh = SegmentTree(n)  # 当前行的线段树
            for j in reversed(range(n)):
                mn = INF
                g = grid[i][j]

                if i == m - 1 and j == n - 1:
                    mn = 1

                if j + 1 <= min(j + g, n - 1):
                    mn = min(mn, minh.query(1, 1, n, j + 2, min(j + g + 1, n)) + 1)

                if i + 1 <= min(i + g, m - 1):
                    mn = min(mn, minl[j].query(1, 1, m, i + 2, min(i + g + 1, m)) + 1)

                if mn < INF:
                    minh.update(1, 1, n, j + 1, mn)
                    minl[j].update(1, 1, m, i + 1, mn)

                if i == 0 and j == 0:
                    ans = mn

        return -1 if ans == INF else ans

1702.修改后的最大二进制字符串

1702. 修改后的最大二进制字符串

猜了一波然后错了

python 复制代码
        '''猜错了!
        l=len(binary)
        if binary=='01':
            return '01'


        if int(binary)==0:
            s='1'*(l-1)+'0'
            return s

        if sum(map(int,list(binary)))==l:
            return binary

        

        x=int(l/2)

        s='1'*x+'0'+'1'*(l-x-1)
        return s
        '''

然后我开始观察这两个操作对二进制串的影响

操作1.将00转为10,可以变大

操作2.将10转为01,这会变小啊?

所以操作2存在的意义就是为了操作1:将0往前推,从而产生操作1的条件

于是自以为是的我就直接把0全往开头放然后进行操作1

python 复制代码
        '''
        c0=binary.count('0')
        #特判!!????
        if c0==1:
            return binary

        if c0==0:
            return binary

        c1=binary.count('1')

        s='1'*(c0-1)+'0'+'1'*c1
        return s
        '''

但是有没有一种可能原本前面1呆的好好的被你往后推了?

比如111000变成000111变成

110111,明显变小了因为第三位的变化,所以这是明显不可取的

我们得从第一个0开始变

python 复制代码
class Solution:
    def maximumBinaryString(self, binary: str) -> str:
        l=len(binary)
        c0=bianry.count('0')
        c1=binary.count('1')
        if c0==0:
            return binary
        for i in range(l):#找第一个0
            if binary[i]=='0':
                x=i
                break

        c12=c1-x
        s=x*'1'+'1'*(c0-1)+'0'+'1'*c12
        return s
相关推荐
Echo``16 分钟前
4:点云处理—去噪、剪切、调平
c++·图像处理·人工智能·算法·机器学习·计算机视觉
#guiyin1126 分钟前
人脸真假检测:SVM 与 ResNet18 的实战对比
算法·机器学习·支持向量机
航Hang*36 分钟前
C PRIMER PLUS——第6-2节:二维数组与多维数组
c语言·开发语言·经验分享·程序人生·算法·学习方法·visual studio
yuhao__z1 小时前
代码随想录算法训练营第六十天| 图论7—卡码网53. 寻宝
算法·图论
AI Echoes1 小时前
大模型(LLMs)强化学习——RLHF及其变种
人工智能·深度学习·算法·机器学习·chatgpt
Dovis(誓平步青云)1 小时前
精讲C++四大核心特性:内联函数加速原理、auto智能推导、范围for循环与空指针进阶
c语言·开发语言·c++·笔记·算法·学习方法
椰萝Yerosius2 小时前
[题解]2023CCPC黑龙江省赛 - Folder
算法
wang__123002 小时前
力扣2680题解
算法·leetcode·职场和发展
GGBondlctrl3 小时前
【leetcode】《BFS扫荡术:如何用广度优搜索征服岛屿问题》
算法·leetcode·bfs·宽度优先·图像渲染·岛屿的数量·被围绕的区域
容辞7 小时前
算法-贪婪算法
算法·贪心算法