这是2026年3月GESP认证考试C++四级的一道编程题,题目如下:
试题描述
现有一片山地,可以视为一个 $N$ 行 $M$ 列的网格图,第 $i$ 行 $j$ 列的海拔为 $h_{i,j}$。
如果一个单元格的海拔不高于其所有相邻单元格(相邻包括上、下、左、右、左上、右上、左下、右下,最多 $8$ 个方向)的海拔,则称该单元格为山谷。
请你数一数该片山地中有多少山谷。
输入说明
第一行包含 $2$ 个整数 $N, M$,表示山地的大小。之后 $N$ 行,每行包含 $M$ 个整数 $h_{i,1}, h_{i,2}, \cdots, h_{i,M}$,表示海拔。
输出说明
输出 $1$ 行,包含 $1$ 个整数 $C$,表示山谷的数量。
数据范围
保证 $1 \leq N, M \leq 100$,$1 \leq h_{i,j} \leq 10^5$。
输入样例
3 5
7 6 6 7 9
6 5 6 7 6
6 5 7 8 9
输出样例
3
这道题该怎么来做呢?我们先把参考实现放在这里,然后就着题目和这个参考实现来讲解实现思路。
#include <iostream>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
int h[105][105];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> h[i][j];
}
}
// 8个方向
int dx[8] = {-1, 1, 0, 0, -1, -1, 1, 1};
int dy[8] = {0, 0, -1, 1, -1, 1, -1, 1};
int count = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
bool isValley = true;
for (int k = 0; k < 8; k++) {
int ni = i + dx[k];
int nj = j + dy[k];
// 判断邻居是否在网格范围内
if (ni >= 0 && ni < n && nj >= 0 && nj < m) {
// 如果当前格子比某个邻居高,就不是山谷
if (h[i][j] > h[ni][nj]) {
isValley = false;
break;
}
}
}
if (isValley) {
count++;
}
}
}
cout << count << endl;
return 0;
}
我们先来看这道题,这道题给你一个 N × M 的表格,每个格子里有一个海拔高度。
现在要找“山谷”。
一个格子是山谷的条件是:
它的海拔不能比周围相邻格子更高。
这里“相邻”包括 8 个方向:
- 上
- 下
- 左
- 右
- 左上
- 右上
- 左下
- 右下
也就是说,一个格子周围最多有 8 个邻居。
怎么判断一个格子是不是山谷
假设现在看某个格子 (i, j)。
我们把它周围所有存在的邻居都检查一遍:
- 如果发现有一个邻居比它更低 那这个格子就不是山谷
- 如果所有邻居都不比它低 那它就是山谷
所以这题其实很直接:
把每个格子都检查一次。
为什么这样做可以
题目要求的是:
当前格子的海拔不高于所有相邻格子的海拔
“不高于”就是:
- 可以更低
- 可以相等
- 但不能更高
所以程序里写的是:
if (h[i][j] > h[ni][nj])
意思是: 如果当前格子比某个邻居高,那它就不合格。
8个方向怎么表示
代码里用了两个数组:
int dx[8] = {-1, 1, 0, 0, -1, -1, 1, 1};
int dy[8] = {0, 0, -1, 1, -1, 1, -1, 1};
它们配合起来表示 8 个方向:
(-1, 0)上(1, 0)下(0, -1)左(0, 1)右(-1, -1)左上(-1, 1)右上(1, -1)左下(1, 1)右下
如果当前格子是 (i, j),那相邻格子就是:
ni = i + dx[k];
nj = j + dy[k];
为什么要判断边界
因为不是每个格子都有 8 个邻居。 比如左上角的格子:
- 没有上面
- 没有左边
- 没有左上角
所以要先判断这个邻居坐标是不是合法:
if (ni >= 0 && ni < n && nj >= 0 && nj < m)
只有在范围内,才能比较。
用样例来理解
输入:
3 5
7 6 6 7 9
6 5 6 7 6
6 5 7 8 9
表格是:
7 6 6 7 9
6 5 6 7 6
6 5 7 8 9
我们找山谷。
第一个山谷:第二行第二列,值是 5
周围的数有:
7 6 6
6 6
6 5 7
这些数都不比 5 小,所以它是山谷。
第二个山谷:第三行第二列,值是 5
周围的数有:
6 5 6
6 7
也没有比 5 更小的,所以它也是山谷。
第三个山谷:第二行第五列,值是 6
周围的数有:
7 9
7
8 9
这些都不比 6 小,所以它也是山谷。
所以答案是:
3
一句话总结
这题的核心就是: 枚举每个格子,检查它周围 8 个方向,只要它不比任何邻居高,它就是山谷。