雷火笔试题第三题
[笔试题]雷火2026/3/21笔试题第三题
T1:略
T2:忘了
T3:最优路径不好找,后面让AI讲解一下吧
T4:模拟金铲铲。。。,很麻烦后面也没时间debug了
T3题目描述
在一个 n × m 的网格中:
- 给定起始点坐标
(b_x, b_y)和终点坐标(e_x, e_y) - 依次放入
q个障碍物,每次输入一个障碍物坐标(x, y) - 每次放入障碍物后需要判断并输出:
- 如果存在从起点到终点的通路(不经过任何障碍物),输出
OK - 如果不存在通路,输出
BLOCKED,然后输出一条允许经过障碍物的最优路径
- 如果存在从起点到终点的通路(不经过任何障碍物),输出
最优路径优先级
当需要输出经过障碍物的路径时,按以下优先级选择:
- 障碍数最少(经过的障碍物格子数量最少)
- 路径最短(总步数最少)
- 字典序最小(路径坐标序列的字典序最小)
解题思路
算法框架
每次放入障碍物后,执行两次 BFS:
放入障碍物
↓
BFS 判断连通性(只能走空地)
↓
有通路?──有──→ 输出 "OK"
│
无
↓
输出 "BLOCKED"
↓
BFS 找最优路径(可以走障碍物)
↓
输出路径坐标
核心算法详解
1. 普通 BFS - 判断连通性
bool bfs_blocked() {
vector<vector<bool>> visited(n, vector<bool>(m, false));
queue<pair<int,int>> q;
q.push({b_x, b_y});
visited[b_x][b_y] = true;
while (!q.empty()) {
auto [x, y] = q.front();
q.pop();
if (x == e_x && y == e_y) return true;
for (int i = 0; i < 4; i++) {
int nx = x + dx[i], ny = y + dy[i];
// 只能走 grid[nx][ny] == 0 的格子
if (isValid(nx, ny) && !visited[nx][ny] && grid[nx][ny] == 0) {
visited[nx][ny] = true;
q.push({nx, ny});
}
}
}
return false;
}
要点:
- 标准 BFS 模板
- 只能走值为 0 的格子(空地)
- 能到达终点返回
true
2. 0-1 BFS 变种 - 找最优路径
void bfs_optimal_path() {
// dist[x][y] = {经过的障碍数,路径长度}
vector<vector<pair<int,int>>> dist(n, vector<pair<int,int>>(m, {INF, INF}));
vector<vector<pair<int,int>>> parent(n, vector<pair<int,int>>(m, {-1, -1}));
queue<tuple<int,int,int,int>> q; // {障碍数,长度,x, y}
dist[b_x][b_y] = {0, 0};
q.push({0, 0, b_x, b_y});
while (!q.empty()) {
auto [obs, len, x, y] = q.front();
q.pop();
// 跳过非最优状态
if (obs > dist[x][y].first || (obs == dist[x][y].first && len > dist[x][y].second))
continue;
// 按字典序尝试四个方向
for (auto [dxi, dyi] : dirs) {
int nx = x + dxi, ny = y + dyi;
if (isValid(nx, ny)) {
int new_obs = obs + grid[nx][ny]; // 是障碍物则 +1
int new_len = len + 1;
// 三层优先级判断
bool better = false;
if (new_obs < dist[nx][ny].first) {
better = true; // 障碍数更少
} else if (new_obs == dist[nx][ny].first && new_len < dist[nx][ny].second) {
better = true; // 障碍数相同,路径更短
}
if (better) {
dist[nx][ny] = {new_obs, new_len};
parent[nx][ny] = {x, y};
q.push({new_obs, new_len, nx, ny});
}
}
}
}
// 回溯路径并输出
// ...
}
要点:
- 状态定义为
{障碍数,路径长度}的二元组 - 每次扩展时,如果走到障碍物格子,障碍数 +1
- 严格按优先级更新状态
3. 字典序最小的处理
// 方向按字典序排列:先尝试 x 小的,再尝试 y 小的
vector<pair<int,int>> dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// 顺序含义:上 -> 下 -> 左 -> 右
通过固定方向的遍历顺序,保证在多条最优路径中,优先找到字典序最小的那条。
数据结构设计
| 变量 | 类型 | 含义 |
|---|---|---|
grid[n][m] |
vector<vector<int>> |
网格,0=空地,1=障碍物 |
dist[n][m] |
vector<vector<pair<int,int>>> |
到达每个点的最优状态 |
parent[n][m] |
vector<vector<pair<int,int>>> |
路径回溯用的父节点 |
dx[], dy[] |
int[] |
方向数组 |
测试用例
用例 1:简单情况 - 始终有通路
输入:
3 3
0 0 2 2
2
1 1
0 1
说明:3×3 网格,起点 (0,0),终点 (2,2),放入 2 个障碍物
用例 2:完全封锁
输入:
3 3
0 0 2 2
3
1 0
1 1
1 2
说明:第二行被完全堵住,需要输出经过障碍的路径
用例 3:直接挡住终点
输入:
2 2
0 0 0 1
1
0 1
说明:终点直接被障碍物覆盖
用例 4:大型网格
输入:
5 5
0 0 4 4
5
2 2
2 3
3 2
3 3
2 1
说明:5×5 网格,多个障碍物
用例 5:一维网格
输入:
1 5
0 0 0 4
2
0 2
0 3
说明:1×5 的长条形网格,障碍物 blocking 路径
用例 6:起点终点重合(边界情况)
输入:
3 3
1 1 1 1
1
0 0
说明:起点和终点是同一个位置
用例 7:无障碍物
输入:
3 3
0 0 2 2
0
说明:没有障碍物,直接判断初始连通性
复杂度分析
| 操作 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 普通 BFS | O(n × m) | O(n × m) |
| 最优路径 BFS | O(n × m) | O(n × m) |
| 单次查询总计 | O(n × m) | O(n × m) |
| q 次查询 | O(q × n × m) | O(n × m) |
注意事项
- C++ 版本要求:代码使用结构化绑定(
auto [x, y]),需要 C++17 - 坐标范围:输入坐标从 0 开始,需要检查边界
- 障碍物不会与起点重合:题目保证
- 路径输出格式:输出时排除起点和终点,中间经过的坐标每行一个,格式为
x y

浙公网安备 33010602011771号