1 条题解
-
0
你好!我是你的 OI 教练。
这道题(NOIP 2015 普及组 T2)是一道非常经典的二维数组模拟题,考察的是对网格图的处理能力。
这类题目在以后学习搜索算法(BFS/DFS)时会经常遇到,所以打好基础很重要。下面是几个关键的思路提示:
1. 核心任务:遍历与计数
你需要输出一个和输入一样大的矩阵。
- 遍历:写一个双重循环,遍历每一个格子 。
- 判断:
- 如果当前格子是雷 (
*):直接输出*。 - 如果当前格子是问号 (
?):你需要统计它周围 8 个方向一共有多少颗雷,然后输出这个数字。
- 如果当前格子是雷 (
2. 难点:如何优雅地检查“周围 8 个格”?
笨办法是写 8 个
if语句:if (a[i-1][j-1] == '*') cnt++; // 左上 if (a[i-1][j] == '*') cnt++; // 上 ...这样做代码很长,容易写错,而且看起来不专业。
推荐写法:方向数组 (Direction Array) 我们可以定义两个小数组,分别表示 轴和 轴的偏移量:
// 对应 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开始),可能会越界或者访问到垃圾数据。 - 解决方法:
- 判定法:在访问
map[nx][ny]之前,先检查nx是否在1~n之间,ny是否在1~m之间。 - 填坑法(推荐新手):把数组开大一点(比如
char a[105][105]),并且把整个数组初始化为非雷字符(比如.)。读入数据时从下标 1 开始读。这样,第 0 行和第 行虽然不是题目给的数据,但它们不是雷,不会影响计数,也不用担心越界报错。
- 判定法:在访问
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
- 上传者