方向数组 + 循环处理邻格。
- 这是一个非常重要的算法习惯,它不仅简洁,还能快速拓展到“上下左右”、“八个方向”、“马走日”等等。
所谓方向数组 + 循环处理邻格,是算法竞赛和日常刷题中非常高频、非常高效的一种技巧。
✅ 先理解什么是方向数组
我们来处理八个方向:
上、下、左、右、左上、右上、左下、右下。
如果你不用方向数组,就会写成:
cpp
复制编辑
if (a[i-1][j] == ...) // 上
if (a[i+1][j] == ...) // 下
if (a[i][j-1] == ...) // 左
if (a[i][j+1] == ...) // 右
if (a[i-1][j-1] == ...) // 左上
if (a[i-1][j+1] == ...) // 右上
if (a[i+1][j-1] == ...) // 左下
if (a[i+1][j+1] == ...) // 右下
写得又臭又长。
✅ 用方向数组写法(推荐)
我们定义两个数组:
cpp
复制编辑
int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
int dy[8] = {-1, 0, 1,-1, 1,-1, 0, 1};
含义是:
(dx[i], dy[i])就是第i个方向的坐标偏移。- 第 0 个方向:
(-1, -1)代表左上角。 - 第 1 个方向:
(-1, 0)代表上。 - ...
我们就可以这样循环访问周围的 8 个邻格:
cpp
复制编辑
for (int d = 0; d < 8; ++d) {
int ni = i + dx[d];
int nj = j + dy[d];
// 判断边界
if (ni >= 0 && ni < 9 && nj >= 0 && nj < 9) {
// 处理邻格
next[ni][nj] += ...;
}
}
✅ 拓展:上下左右(四个方向)
只想处理上下左右?就用:
cpp
复制编辑
int dx[4] = {-1, 1, 0, 0};
int dy[4] = { 0, 0,-1, 1};
✅ 拓展:马走日(象棋/国际象棋)
cpp
复制编辑
int dx[8] = {-2,-2,-1,-1, 1, 1, 2, 2};
int dy[8] = {-1, 1,-2, 2,-2, 2,-1, 1};
✅ 拓展:八方向的 BFS(如迷宫扩散、走棋)
方向数组和 BFS、DFS 结合,可以写得非常整洁、清晰、可拓展。
🧠 总结一句话:
方向数组 + 循环 是“邻接格处理”问题的终极武器,能让你从 if 地狱中解放出来,
不管你是做模拟、递归、搜索、游戏模拟、迷宫走法,只要涉及“周围 4 或 8 个格子”,几乎都能用!
✅ 练习题 1:统计细胞块个数(经典方向数组 + DFS)
📄 题目描述:
给你一个 9×9 的网格(可以用 0 和 1 表示),
其中:
1表示细胞,0表示空白。
如果两个 1 在八个方向上相邻,就认为它们属于同一个细胞块。
请你计算出整个网格中,一共有多少个细胞块。
🎯 输入格式:
9 行,每行 9 个字符(0 或 1)
txt
复制编辑
001000000
011000100
000000000
001000000
001100000
000000000
000011100
000011100
000000000
🎯 输出格式:
输出一个整数,表示细胞块的个数。
复制编辑
3
✅ 解题目标:
- 使用方向数组
dx[8]dy[8]。 - 用 DFS 或 BFS 搜索相邻的
1,标记为“已访问”。 - 每当你找到一个新的没访问的
1,就计数 +1,启动一次 DFS。
✅ 练习题 2:水池边界判定(只用上下左右方向)
📄 题目描述:
给你一个 5×5 的地图(输入为字符):
.表示陆地~表示水
一个水池由一片 ~ 相连的区域组成(只看上下左右方向相邻)。
请你输出地图上是否存在**“被陆地完全包围”的水池**(也就是说,水池没有连接地图边界)。
示例输入:
txt
复制编辑
.....
.~~~.
.~~~.
.~~~.
.....
输出:
yaml
复制编辑
Yes
示例输入:
txt
复制编辑
~~..~
~~..~
.....
.....
.....
输出:
yaml
复制编辑
No
✅ 解题目标:
- 只用
dx[4]dy[4]。 - 用 DFS 从每个
~开始搜索。 - 判断这一片水池有没有连到边界。
- 如果有一个水池完全没碰边界,那就输出
Yes,否则No。
💡 提示:
八方向:
cpp
复制编辑
int dx[8] = {-1,-1,-1,0,0,1,1,1};
int dy[8] = {-1,0,1,-1,1,-1,0,1};
四方向:
cpp
复制编辑
int dx[4] = {-1,1,0,0};
int dy[4] = {0,0,-1,1};
✅ 题目再复述一下
地图是一个 5×5 的字符网格:
~表示水。.表示陆地。- 水池:一块连在一起的
~。 - 只看上下左右是否相邻。
你要判断是否存在一个完整被陆地包围的水池(不连接地图边界)。
✅ 示例一
txt
复制编辑
.....
.~~~.
.~~~.
.~~~.
.....
👉 水池没有接触边界 → 输出:Yes
✅ 示例二
txt
复制编辑
~~..~
~~..~
.....
.....
.....
👉 水池从左上角直接连到边界 → 输出:No
✅ 解题思路(DFS版本):
- 建立地图
char grid[5][5];。 - 建立
visited[5][5]数组。 - 对每个没访问过的
~发起 DFS,构成一个水池。 - 在 DFS 过程中记录是否碰到了边界。
- 如果某个水池完全没碰边界,就说明它是被包围的,输出
Yes。 - 否则,最后输出
No。
✅ 方向数组(上下左右):
cpp
复制编辑
int dx[4] = {-1, 1, 0, 0};
int dy[4] = { 0, 0,-1, 1};
✅ 模板代码结构(你可以自己写,也可以填空):
cpp
复制编辑
#include <iostream>
using namespace std;
char grid[5][5];
bool visited[5][5];
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
bool touchBorder; // 这个变量表示当前水池是否接触到边界
void dfs(int x, int y) {
visited[x][y] = true;
// 判断是否在边界上
if (x == 0 || x == 4 || y == 0 || y == 4)
touchBorder = true;
for (int d = 0; d < 4; d++) {
int nx = x + dx[d];
int ny = y + dy[d];
if (nx >= 0 && nx < 5 && ny >= 0 && ny < 5) {
if (!visited[nx][ny] && grid[nx][ny] == '~') {
dfs(nx, ny);
}
}
}
}
int main() {
// 读入地图
for (int i = 0; i < 5; i++)
cin >> grid[i];
// 枚举所有格子
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (!visited[i][j] && grid[i][j] == '~') {
touchBorder = false;
dfs(i, j);
if (!touchBorder) {
cout << "Yes" << endl;
return 0;
}
}
}
}
cout << "No" << endl;
return 0;
}
✅ 推荐你现在去做的步骤:
-
自己独立敲一遍。
-
用两组数据测试:
Yes:中间的水池。No:角落的水池。
如果你愿意挑战一下,还可以试试改成 BFS 版本(我也可以提供指导)。