适合谁、学完能做什么
适合已经会使用变量、分支、循环和一维数组的学习者。学完后,你应该能够:
- 用
a[i][j]表示第i行、第j列的格子。 - 正确完成二维数组的按行、按列遍历。
- 把“上、下、左、右”转换为坐标变化。
- 在移动前判断新坐标是否越界。
- 处理地图、棋盘、座位表、矩阵和网格状态变化。
前置知识与网站关联
必须掌握变量、条件判断、循环、一维数组和基本输入输出。
对应地图节点:arrays.two(二维数组)、arrays.grid(网格模拟)、arrays.direction(方向移动)、arrays.coords(坐标与边界)、arrays.5855c61cec(模拟)。
配套专题:CSP-J|数学|模拟|。
问题引入
下面的地图有4行5列:
列0 列1 列2 列3 列4
行0 . . # . .
行1 . # # . .
行2 . . . . #
行3 # . . . .
a[1][2] 表示第1行、第2列的格子。地图、棋盘和矩阵都可以用这种“行坐标+列坐标”表示。
核心概念与图示
如果网格有 n 行、m 列,合法坐标是:
0 <= 行 < n,0 <= 列 < m
按行遍历:
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
// 处理 a[i][j]
四方向变化量如下:
| 方向 | 新行 | 新列 |
|---|---|---|
| 上 | x-1 | y |
| 下 | x+1 | y |
| 左 | x | y-1 |
| 右 | x | y+1 |
代码可写成:
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
从 (x,y) 走第 k 个方向后,新坐标是 (x+dx[k], y+dy[k])。访问前必须先检查:
0 <= nx && nx < n && 0 <= ny && ny < m
示例问题
给定一个由 # 和 . 组成的网格,求每个格子周围四个方向上有多少个 # 格子。
处理每个格子的过程是:遍历当前格子,依次枚举四个方向,计算相邻坐标,越界就跳过,合法且内容为 # 时计数加一。
算法与正确性
四方向数组恰好生成当前格子的四个相邻位置。边界判断排除了所有非法位置;对每个合法邻居只在内容为 # 时计数,因此得到的数量恰好是四邻域中 # 格子的数量。所有格子都执行同样的过程,整体结果正确。
完整 C++ 示例
语言:C++17。输入 n m 和 n 行网格,输出每个格子的四邻域 # 数量。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<string> grid(n);
for (int i = 0; i < n; ++i) cin >> grid[i];
const int dx[4] = {-1, 1, 0, 0};
const int dy[4] = {0, 0, -1, 1};
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
int count = 0;
for (int k = 0; k < 4; ++k) {
int ni = i + dx[k];
int nj = j + dy[k];
if (ni < 0 || ni >= n || nj < 0 || nj >= m) continue;
if (grid[ni][nj] == '#') ++count;
}
if (j > 0) cout << ' ';
cout << count;
}
cout << '\n';
}
return 0;
}
复杂度与适用范围
设网格有 n 行、m 列。每个格子最多检查4个方向,因此时间复杂度为 O(nm),存储网格需要 O(nm) 空间。
适合地图、棋盘、邻域统计、矩阵变换和网格状态更新。如果题目要求从起点走到终点、求最少步数或统计连通区域,还需要 BFS、DFS 或动态规划。
易错点
- 行列写反:统一使用
a[行][列],变量名用i表示行、j表示列。 - 先访问再判断边界:必须先检查
ni、nj,再访问grid[ni][nj]。 - 四方向误写成八方向:斜对角是否计入,要以题目定义为准。
- 边计算边覆盖原状态:如果要求同时更新所有格子,应使用新数组保存下一时刻状态。
配套练习
以下题目来自本地 CSP-J|数学|模拟| 专题,题名、链接和难度已核对。当前内容工作室没有这些题的已发布题解,均只提供原题入口。
基础
- P7186:[CRCI2008-2009] TABLICA(普及):练习矩阵行列处理和规则化输出。
- P3392:涂条纹(普及-):练习二维网格中的区域统计。
变式
- P1056:[NOIP 2008 普及组] 排座椅(普及):练习二维关系到行、列统计的转换。
- P9582:「MXOI Round 1」方格(普及-):练习方格坐标、邻接关系和边界处理。
综合
- P11228:[CSP-J 2024] 地图探险(普及-):综合练习地图、方向移动、状态记录和边界判断。
自查与小结
完成本篇后,应能回答:a[i][j] 中哪个下标是行?如何表示四方向?为什么必须先判断边界?四方向和八方向有什么区别?为什么同时更新网格时通常要用新数组?
能够独立写出二维数组遍历、四方向移动和边界判断,并完成两道基础题和一道综合题,就可以继续学习网格 DFS/BFS 或二维前缀和。