这是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 个方向,只要它不比任何邻居高,它就是山谷。

文章分享二维码

微信扫码分享这篇文章

扫描二维码即可在手机中继续阅读,也方便转发给老师、家长或同学。

上一篇 已经是最新文章
下一篇 人工智能时代,为什么编程正在成为孩子的基础能力