带扩展单元格的旅行图多源BFS问题求解咨询
嘿,我来帮你搞定这个网格扩展的问题!你之前用多源BFS的思路方向是对的,但状态设计里缺了对扩展次数的跟踪,也没提前判断扩展/移动的合法性,所以才会卡壳。咱们一步步拆解调整:
先理清楚核心规则
首先得把题目里的规则嚼透,不然思路容易跑偏:
- 你控制的探索者从任意起点出发,每单位时间能上下左右走一格,走到的单元格会被标记为已占用。
- 每累计过T单位时间,所有已占用的单元格会集体向四方向扩展一圈(也就是每个已占用格的上下左右都变成已占用)。
- 不管是探索者移动到了不可访问区域/网格外,还是扩展出来的单元格碰到了这些非法区域,整个过程直接停止,最后统计所有被占用过的不同单元格总数。
调整你的BFS状态设计
你之前的[M][M][K]状态只记录了位置和距下次扩展的剩余时间,这不够。咱们把状态改成**(x, y, remaining_time, expand_count)**:
x,y:探索者当前的位置remaining_time:距离下一次扩展还剩多少时间(范围0到T-1)expand_count:已经完成了多少次扩展
这个状态能帮你精准跟踪什么时候要触发扩展,以及当前的扩展轮次,方便判断后续操作是否合法。
关键的合法性判断逻辑
这是解决问题的核心,分两种情况处理:
1. 移动时的合法性判断
当探索者要移动到相邻单元格(nx, ny)时:
- 先检查
(nx, ny)是不是在网格范围内,而且不是不可访问单元格。要是不满足,说明移动后直接触发停止条件,这个路径就别往下走了。 - 要是合法,就把
(nx, ny)标记为已占用(没标记过的话),然后生成新状态:(nx, ny, remaining_time-1, expand_count)(因为过了1单位时间,剩余时间减1)。
2. 扩展时的合法性判断
当remaining_time == 0时,下一个时间单位就要触发扩展了,这时候得提前预判这次扩展会不会踩雷:
- 咱们可以提前给每个单元格算个
max_expand值:这个值代表以该单元格为中心,最多能向外扩展多少次而不碰到障碍或边界。比如边界单元格或不可访问单元格的max_expand是0,内部单元格的max_expand等于它四个邻居的max_expand最小值加1(用多源BFS就能快速算出这个矩阵)。 - 然后,全局的最大可扩展次数是所有已占用单元格的
max_expand中的最小值。如果下一次扩展的次数(expand_count+1)超过了这个最小值,说明扩展后会碰到非法区域,直接停止这个路径的后续操作。 - 要是扩展合法,就把所有已占用单元格的四方向相邻格标记为已占用(注意去重),然后生成新状态:
(x, y, T-1, expand_count+1)(扩展消耗了1单位时间,剩余时间重置为T-1)。
优化技巧(应对M=1000的大网格)
- 预处理
max_expand矩阵:用多源BFS一次性算出所有单元格的最大可扩展次数,避免每次判断扩展合法性时遍历整个网格。 - 状态去重:用一个三维数组
visited[x][y][remaining_time],记录该位置在剩余时间为remaining_time时已经达到的最大扩展次数。如果当前状态的expand_count不大于记录的次数,就跳过这个状态,避免重复处理。 - 维护全局最小
max_expand:不用每次遍历整个网格找全局最小,而是在每次标记新的已占用单元格时,更新全局的最小max_expand值,这样能大幅提升效率。
伪代码参考
def calculate_occupied_cells(M, grid, starts, T): # grid: 0=可占用,1=不可访问 occupied = [[False]*M for _ in range(M)] # 预处理每个单元格的最大可扩展次数max_expand max_expand = [[0]*M for _ in range(M)] from collections import deque q = deque() # 边界和不可访问单元格的max_expand为0,加入队列初始化 for i in range(M): for j in range(M): if grid[i][j] == 1 or i in (0, M-1) or j in (0, M-1): max_expand[i][j] = 0 q.append((i, j)) else: max_expand[i][j] = -1 # 未初始化标记 dirs = [(-1,0), (1,0), (0,-1), (0,1)] # 多源BFS计算max_expand while q: x, y = q.popleft() for dx, dy in dirs: nx, ny = x+dx, y+dy if 0<=nx<M and 0<=ny<M and max_expand[nx][ny] == -1: max_expand[nx][ny] = max_expand[x][y] + 1 q.append((nx, ny)) # 初始化BFS队列,状态为(x,y,剩余时间,已扩展次数) bfs_q = deque() # visited[x][y][剩余时间] 记录该状态下的最大扩展次数,避免重复处理 visited = [[[-1]*T for _ in range(M)] for _ in range(M)] # 初始化全局最小max_expand global_min_expand = float('inf') for sx, sy in starts: occupied[sx][sy] = True bfs_q.append((sx, sy, T-1, 0)) visited[sx][sy][T-1] = 0 if max_expand[sx][sy] < global_min_expand: global_min_expand = max_expand[sx][sy] total = len(starts) while bfs_q: x, y, remaining, expand = bfs_q.popleft() # 处理移动操作 for dx, dy in dirs: nx, ny = x+dx, y+dy if 0<=nx<M and 0<=ny<M and grid[nx][ny]==0 and not occupied[nx][ny]: occupied[nx][ny] = True total +=1 # 更新全局最小max_expand if max_expand[nx][ny] < global_min_expand: global_min_expand = max_expand[nx][ny] new_remaining = remaining -1 new_expand = expand # 状态去重判断 if visited[nx][ny][new_remaining] < new_expand: visited[nx][ny][new_remaining] = new_expand bfs_q.append((nx, ny, new_remaining, new_expand)) # 处理扩展操作(剩余时间为0时触发) if remaining == 0: if expand +1 > global_min_expand: # 扩展会触发停止条件,跳过 continue # 执行扩展,收集所有新的可占用单元格 temp_new = set() for i in range(M): for j in range(M): if occupied[i][j]: for dx, dy in dirs: ni, nj = i+dx, j+dy if 0<=ni<M and 0<=nj<M and grid[ni][nj]==0 and not occupied[ni][nj]: temp_new.add((ni, nj)) # 标记新单元格并更新总数 for ni, nj in temp_new: occupied[ni][nj] = True total +=1 if max_expand[ni][nj] < global_min_expand: global_min_expand = max_expand[ni][nj] # 生成新状态 new_remaining = T-1 new_expand = expand +1 if visited[x][y][new_remaining] < new_expand: visited[x][y][new_remaining] = new_expand bfs_q.append((x, y, new_remaining, new_expand)) return total
这个伪代码里已经优化了全局最小max_expand的维护,不用每次遍历整个网格,而且状态去重也做了处理,应对M=1000的大网格也没问题。
内容的提问来源于stack exchange,提问作者cracra
相关产品推荐
相关产品推荐

