1 条题解

  • 0
    @ 2025-11-28 10:53:35

    你好!我是你的 OI 教练。

    这道题(NOIP 2015 普及组 T2)是一道非常经典的二维数组模拟题,考察的是对网格图的处理能力。

    这类题目在以后学习搜索算法(BFS/DFS)时会经常遇到,所以打好基础很重要。下面是几个关键的思路提示:

    1. 核心任务:遍历与计数

    你需要输出一个和输入一样大的矩阵。

    • 遍历:写一个双重循环,遍历每一个格子 (i,j)(i, j)
    • 判断
      • 如果当前格子是雷 (*):直接输出 *
      • 如果当前格子是问号 (?):你需要统计它周围 8 个方向一共有多少颗雷,然后输出这个数字。

    2. 难点:如何优雅地检查“周围 8 个格”?

    笨办法是写 8 个 if 语句:

    if (a[i-1][j-1] == '*') cnt++; // 左上
    if (a[i-1][j] == '*') cnt++;   // 上
    ...
    

    这样做代码很长,容易写错,而且看起来不专业。

    推荐写法:方向数组 (Direction Array) 我们可以定义两个小数组,分别表示 xx 轴和 yy 轴的偏移量:

    // 对应 8 个方向:左上、上、右上、左、右、左下、下、右下
    int dx[8] = {-1, -1, -1,  0, 0,  1, 1, 1};
    int dy[8] = {-1,  0,  1, -1, 1, -1, 0, 1};
    

    这样,你只需要写一个循环 k 从 0 到 7:

    • 邻居的横坐标 nx = i + dx[k]
    • 邻居的纵坐标 ny = j + dy[k] 然后检查 map[nx][ny] 即可。

    3. 易错点:数组越界 (Boundary Check)

    在检查周围格子时,必须非常小心。

    • 例如:对于第 1 行的格子,它的“上面”是没有格子的。如果你尝试访问 map[0][5](假设下标从1开始),可能会越界或者访问到垃圾数据。
    • 解决方法
      1. 判定法:在访问 map[nx][ny] 之前,先检查 nx 是否在 1~n 之间,ny 是否在 1~m 之间。
      2. 填坑法(推荐新手):把数组开大一点(比如 char a[105][105]),并且把整个数组初始化为非雷字符(比如 .)。读入数据时从下标 1 开始读。这样,第 0 行和第 n+1n+1 行虽然不是题目给的数据,但它们不是雷,不会影响计数,也不用担心越界报错。

    4. 代码逻辑框架

    #include <iostream>
    using namespace std;
    
    // 1. 定义大一点的二维数组
    char a[105][105]; 
    
    // 定义方向数组
    int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
    int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
    
    int main() {
        int n, m;
        cin >> n >> m;
    
        // 2. 输入数据 (建议从下标 1 开始,方便处理边界)
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                cin >> a[i][j];
            }
        }
    
        // 3. 处理并输出
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                // 如果本来就是雷,直接打印
                if (a[i][j] == '*') {
                    cout << '*';
                } else {
                    // 如果不是雷,开始数数
                    int cnt = 0;
                    for (int k = 0; k < 8; k++) {
                        int nx = i + dx[k];
                        int ny = j + dy[k];
                        
                        // 如果用了“填坑法”且数组够大,这里的边界判断可以省略
                        // 但为了严谨,建议加上:
                        // if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && a[nx][ny] == '*')
                        if (a[nx][ny] == '*') { 
                            cnt++;
                        }
                    }
                    cout << cnt;
                }
            }
            cout << endl; // 记得换行
        }
        return 0;
    }
    

    加油!这道题主要是练习二维坐标的操作,写完你会对坐标系更有感觉。

    • 1

    信息

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