1 条题解

  • 0
    @ 2025-11-28 9:44:29

    你好!我是你的 OI 教练。

    这道题(CSP-J 2024 T2 地图探险)是一道非常标准的 模拟(Simulation) 题目。

    题目要求我们让机器人完全按照设定的规则移动 kk 步,我们要做的就是把这些规则翻译成计算机代码,一步一步执行,并记下它去过多少个不同的地方。

    1. 核心思路:步步为营

    由于 kk 最大只有 10610^6,且 TT(数据组数)只有 5,总计算量在 5×1065 \times 10^6 左右,计算机 1 秒钟可以轻松跑完。所以我们不需要高深的算法,直接写一个循环,模拟 kk 次操作即可。

    2. 关键工具:方向数组

    处理在网格图上移动的问题,最方便的方法是定义两个数组来表示方向的变化。 题目定义的坐标系是:xx 是行,yy 是列。

    • d=0d=0 (东): (x,y+1)(x, y+1)
    • d=1d=1 (南): (x+1,y)(x+1, y)
    • d=2d=2 (西): (x,y1)(x, y-1)
    • d=3d=3 (北): (x1,y)(x-1, y)

    我们可以这样定义:

    // 对应 d = 0, 1, 2, 3
    int dx[4] = {0, 1, 0, -1};
    int dy[4] = {1, 0, -1, 0};
    

    这样,假设当前在 (x,y)(x, y),朝向是 dd,下一步的坐标 (nx,ny)(nx, ny) 就是: nx = x + dx[d]; ny = y + dy[d];

    3. 数据存储与去重

    • 地图存储:用 char g[1005][1005] 存储输入的地图(x.)。
    • 记录去过的地方:我们需要统计有多少个不同的位置。
      • 可以使用一个 bool vis[1005][1005] 数组。
      • 初始化全为 false(或者 0)。
      • 起点的 vis[x0][y0] 设为 true,计数器 ans 初始为 1。
      • 每次成功移动到一个新格子 (nx,ny)(nx, ny) 时,检查 vis[nx][ny]
        • 如果之前没来过(是 false),ans 加 1,并把 vis[nx][ny] 标记为 true
        • 如果之前来过,ans 不变,人移动过去。

    4. 模拟逻辑(循环 k 次)

    每一步(循环一次)的逻辑如下:

    1. 计算预想的下一步位置 nxny
    2. 检查是否合法
      • 是否越界?(1nxn1 \le nx \le n1nym1 \le ny \le m
      • 是否有障碍?(g[nx][ny] == 'x'
    3. 分支判断
      • 情况 A(合法)
        • 人真正移动过去:更新 x = nx, y = ny
        • 统计:如果这里没来过,ans++,标记 vis[nx][ny] = true
        • 朝向 d 不变。
      • 情况 B(不合法)
        • 人原地不动。
        • 向右转:d = (d + 1) % 4;

    5. 易错点提示(坑在哪里?)

    1. 多组数据初始化
      • 题目有 TT 组数据。每处理完一组,记得清空 vis 数组!否则上一组的标记会影响下一组。
      • 可以使用 memset(vis, 0, sizeof(vis));
    2. 坐标越界检查顺序
      • 在判断障碍物之前,必须先判断是否越界。
      • 错误写法:if (g[nx][ny] == 'x' || nx < 1 ...) -> 这会导致数组越界访问。
      • 正确写法:if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && g[nx][ny] != 'x')
    3. 转弯也算一步
      • 题目说了“接下来机器人将要进行 kk 次操作”。
      • 无论是“移动一步”还是“原地转弯”,都消耗一次 kk。不要在转弯的时候忘记循环计数。
    4. 1-based vs 0-based
      • 题目给的坐标是从 1 开始的。
      • 建议你的数组开大一点(比如 1005),并且也从下标 1 开始存,这样逻辑最清晰,不用转换。

    6. 代码结构框架

    #include <iostream>
    #include <cstring> // 为了使用 memset
    using namespace std;
    
    const int MAXN = 1005;
    char g[MAXN][MAXN];
    bool vis[MAXN][MAXN];
    int dx[4] = {0, 1, 0, -1}; // 东南西北
    int dy[4] = {1, 0, -1, 0};
    
    void solve() {
        int n, m, k;
        int x, y, d;
        cin >> n >> m >> k;
        cin >> x >> y >> d;
        
        // 读入地图
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                cin >> g[i][j];
            }
        }
        
        // 初始化访问记录
        memset(vis, 0, sizeof(vis));
        
        // 起点处理
        long long ans = 1; // 起点算一个
        vis[x][y] = true;
        
        // 开始模拟 k 步
        while (k--) {
            // 1. 算出下一步想去哪
            int nx = x + dx[d];
            int ny = y + dy[d];
            
            // 2. 检查能否走过去
            // 条件:在地图内 且 不是障碍物
            if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && g[nx][ny] == '.') {
                // 能走:更新坐标
                x = nx;
                y = ny;
                // 如果是新地方,计数
                if (!vis[x][y]) {
                    vis[x][y] = true;
                    ans++;
                }
            } else {
                // 不能走:原地右转
                d = (d + 1) % 4;
            }
        }
        
        cout << ans << endl;
    }
    
    int main() {
        int t;
        cin >> t;
        while (t--) {
            solve();
        }
        return 0;
    }
    

    试着按照这个逻辑把代码补全并提交吧!加油!

    • 1

    信息

    ID
    15765
    时间
    1000ms
    内存
    32MiB
    难度
    3
    标签
    递交数
    1
    已通过
    1
    上传者