AcWing算法提高课-2.1.3山峰和山谷

算法提高课整理

CSDN个人主页:更好的阅读体验

Start

原题链接
题目描述

FGD 小朋友特别喜欢爬山,在爬山的时候他就在研究山峰和山谷。

为了能够对旅程有一个安排,他想知道山峰和山谷的数量。

给定一个地图,为 FGD 想要旅行的区域,地图被分为 n × n n \times n n×n 的网格,每个格子 ( i , j ) (i,j) (i,j) 的高度 w i , j w_{i,j} wi,j 是给定的。

若两个格子有公共顶点,那么它们就是相邻的格子,如与 ( i , j ) (i,j) (i,j) 相邻的格子有 ( i − 1 , j − 1 ) , ( i − 1 , j ) , ( i − 1 , j + 1 ) , ( i , j − 1 ) , ( i , j + 1 ) , ( i + 1 , j − 1 ) , ( i + 1 , j ) , ( i + 1 , j + 1 ) (i-1, j-1),(i-1,j),(i-1,j+1),(i,j-1),(i,j+1),(i+1,j-1),(i+1,j),(i+1,j+1) (i1,j1),(i1,j),(i1,j+1),(i,j1),(i,j+1),(i+1,j1),(i+1,j),(i+1,j+1)

我们定义一个格子的集合 S S S 为山峰(山谷)当且仅当:

  1. S S S 的所有格子都有相同的高度。
  2. S S S 的所有格子都连通。
  3. 对于 s ∈ S s\in S sS,与 s s s 相邻的 s ’ ∉ S s’\notin S s/S,都有 w s > w s ’ w_s > w_{s’} ws>ws(山峰),或者 w s < w s ’ w_s < w_{s’} ws<ws(山谷)。
  4. 如果周围不存在相邻区域,则同时将其视为山峰和山谷。

你的任务是,对于给定的地图,求出山峰和山谷的数量,如果所有格子都有相同的高度,那么整个地图即是山峰,又是山谷。

输入格式

第一行包含一个正整数 n n n,表示地图的大小。

接下来一个 n × n n \times n n×n 的矩阵,表示地图上每个格子的高度 w w w

输出格式

共一行,包含两个整数,表示山峰和山谷的数量。

数据范围

1 ≤ n ≤ 1000 1 \le n \le 1000 1n1000,
0 ≤ w ≤ 1 0 9 0 \le w \le 10^9 0w109


思路

发现对于每个连通块

  • 四周没有比他高的,他就是山峰;
  • 四周没有比他低的,他就是山谷;
  • 如果都有,那么他啥也不是。

因此考虑 BFS 搜索连通块。

如果一个连通块周围没有比他高的,那他就是山峰。同理判断山谷。

算法时间复杂度 O ( n 2 ) O(n^2) O(n2)
AC Code

C + + \text{C}++ C++

#include <iostream>
#include <queue>

using namespace std;

typedef pair<int, int> PII;
#define x first
#define y second

const int N = 1010;
int dx[8] = {-1, -1, -1, 0, 1, 1, 1, 0}; // 8方向偏移量
int dy[8] = {-1, 0, 1, 1, 1, 0, -1, -1};

int n;
int h[N][N];
queue<PII> q;
bool st[N][N];

void bfs(int x, int y, bool& f1, bool& f2)
{
    q.push({x, y}); // 起点入队
    st[x][y] = 1; // 起点被遍历过

    while (q.size())
    {
        auto t = q.front();
        q.pop();

        for (int i = 0; i < 8; i ++ ) // 8方向扩展
        {
            int xx = t.x + dx[i], yy = t.y + dy[i];
            if (xx < 0 || xx >= n || yy < 0 || yy >= n) continue; // 如果出界就继续
            if (h[xx][yy] != h[t.x][t.y]) // 如果扩展出的和现在的高度不相等
            {
                if (h[xx][yy] > h[t.x][t.y]) f1 = 1; // 高的话是山峰
                else f2 = 1; // 否则是山谷
            }
            else if (!st[xx][yy]) // 如果没被遍历过
            {
                st[xx][yy] = 1;
                q.push({xx, yy}); // 入队
            }
        }
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            scanf("%d", &h[i][j]);

    int r1 = 0, r2 = 0;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            if (!st[i][j]) // 遍历整个图
            {
                bool f1 = 0, f2 = 0;
                bfs(i, j, f1, f2);
                if (!f1) r1 ++ ; // 山峰
                if (!f2) r2 ++ ; // 山谷
            }

    printf("%d %d\n", r1, r2);
    return 0;
}

228aa7bed3e021faf24cf8560d3e47bb.gif

最后,如果觉得对您有帮助的话,点个赞再走吧!

  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星河依旧长明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值