1. 问题描述:
FGD小朋友特别喜欢爬山,在爬山的时候他就在研究山峰和山谷。为了能够对旅程有一个安排,他想知道山峰和山谷的数量。给定一个地图,为FGD想要旅行的区域,地图被分为 n×n 的网格,每个格子 (i,j) 的高度 w(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)。我们定义一个格子的集合 S 为山峰(山谷)当且仅当:
S 的所有格子都有相同的高度。
S 的所有格子都连通。
对于 s 属于 S,与 s 相邻的 s′ 不属于 S,都有 ws>ws′(山峰),或者 ws<ws′(山谷)。
如果周围不存在相邻区域,则同时将其视为山峰和山谷。
你的任务是,对于给定的地图,求出山峰和山谷的数量,如果所有格子都有相同的高度,那么整个地图即是山峰,又是山谷。
输入格式
第一行包含一个正整数 n,表示地图的大小。接下来一个 n×n 的矩阵,表示地图上每个格子的高度 w。
输出格式
共一行,包含两个整数,表示山峰和山谷的数量。
数据范围
1 ≤ n ≤ 1000,
0 ≤ w ≤ 10 ^ 9
输入样例1:
5
8 8 8 7 7
7 7 8 8 7
7 7 7 7 7
7 8 8 7 8
7 8 8 8 8
输出样例1:
2 1
输入样例2:
5
5 7 8 3 1
5 5 7 6 6
6 6 6 2 8
5 7 2 5 8
7 1 0 1 7
输出样例2:
3 3
样例解释
样例1:
样例2:
2. 思路分析:
分析题目可以知道我们需要求解山峰和山谷的数量,每一个山峰或者山谷都是元素全部相等的连通块,如果当前连通块的元素周围的连通块元素没有比它更大的,说明当前连通块是山峰,如果没有比它更小的,说明当前连通块属于山谷,所以我们在搜索连通块的时候需要判断周围连通块的类型,可以使用两个变量记录当前连通块周围是否有比它高或者比它矮的连通块,我们可以使用flood fill算法搜索连通块,根据两个记录的变量计算当前山峰和山谷的数量。
3. 代码如下:
import collections
from typing import List
class Solution:
def bfs(self, x: int, y: int, n: int, g: List[List[int]], st: List[List[int]]):
peak = valley = 0
q = collections.deque([(x, y)])
st[x][y] = 1
while q:
x0, y0 = q.popleft()
for i in range(x0 - 1, x0 + 2):
for j in range(y0 - 1, y0 + 2):
# 搜索的位置是当前的(x0, y0)直接跳过
if i == x0 and j == y0: continue
# 越界了
if i < 0 or i >= n or j < 0 or j >= n: continue
# 元素不相等了说明存在山峰或者山谷
if g[x0][y0] != g[i][j]:
# 进来这个判断说明不是大于就是小于
# 周围有比它小的说明不是当前连通块不是山谷
if g[x0][y0] > g[i][j]:
valley = 1
# 周围有比当前连通块大的说明不是山峰
else:
peak = 1
# 其实是检验3 * 3的小方格, 如果当前的位置的元素未访问那么将其加入到队列中
elif st[i][j] == 0:
q.append((i, j))
st[i][j] = 1
return peak, valley
def process(self):
n = int(input())
g = list()
for i in range(n):
g.append(list(map(int, input().split())))
st = [[0] * (n + 10) for i in range(n + 10)]
peak = valley = 0
for i in range(n):
for j in range(n):
if st[i][j] == 0:
p, v = self.bfs(i, j, n, g, st)
# 没有比当前连通块高的元素, 山峰加1, 没有比当前连通块低的, 山谷加1
if p == 0:
peak += 1
if v == 0:
valley += 1
print(peak, valley)
if __name__ == '__main__':
Solution().process()