STATUS: NOMINAL LOCAL TIME: 00:00:00 返回园内

雷火笔试题第三题

[笔试题]雷火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,然后输出一条允许经过障碍物的最优路径

最优路径优先级

当需要输出经过障碍物的路径时,按以下优先级选择:

  1. 障碍数最少(经过的障碍物格子数量最少)
  2. 路径最短(总步数最少)
  3. 字典序最小(路径坐标序列的字典序最小)

解题思路

算法框架

每次放入障碍物后,执行两次 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)

注意事项

  1. C++ 版本要求:代码使用结构化绑定(auto [x, y]),需要 C++17
  2. 坐标范围:输入坐标从 0 开始,需要检查边界
  3. 障碍物不会与起点重合:题目保证
  4. 路径输出格式:输出时排除起点和终点,中间经过的坐标每行一个,格式为 x y

posted @ 2026-03-21 19:00  猫爹爱猫娘  阅读(27)  评论(0)    收藏  举报