题目描述
译自 POI 2007 Stage 2. Day 0「Ridges and Valleys」 #2653. 「POI2007」山峰和山谷 Ridges and Valleys - 题目 - LibreOJ (loj.ac) 给定一个 的网格状地图,每个方格 有一个高度 。如果两个方格有公共顶点,则它们是相邻的。
定义山峰和山谷如下:
- 均由地图上的一个连通块组成;
- 所有方格高度都相同;
- 周围的方格(即不属于山峰或山谷但与山峰或山谷相邻的格子)高度均大于山谷的高度,或小于山峰的高度。
求地图内山峰和山谷的数量。特别地,如果整个地图方格的高度均相同,则整个地图既是一个山谷,也是一个山峰。
输入格式
第一行一个整数 ,表示地图的大小。
接下来 行每行 个整数表示地图。第 行有 个整数 ,表示地图第 行格子的高度。
输出格式
输出一行两个整数,分别表示山峰和山谷的数量。
样例 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
输出
2 1
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
输出
3 3
1
思路
依然是一个点周围8个点都算连通, 这里需要找山峰的数量和山谷的数量。 山峰是指该点的权值大于周围点的权值的连通点集是一个山峰。 山谷同理, 是该点权值小于周围点权值的连通点集是一个山谷。
同样声明st
数组记录当前点是否被枚举过, 先循环遍历所有点, 遇到未枚举过的点就进行BFS泛洪标记与当前值相同的连通点集, 并判断点集周围是否存在大于的点或者小于的点。
如果周围不存在小于当前权值的点, 说明当前点集为山谷。 若周围不存在大于当前取值的点, 说明当前点集为山峰。
若既不存在大于也不存在小于, 说明该图权值都相等, 即是山谷也是山峰。
代码
const int N = 1e3 + 10;
int g[N][N];
int n;
bool st[N][N];
int res_u, res_n; // 山谷与山峰
typedef pair<int, int> PII;
PII q[N * N];
void bfs(int xx, int yy, bool &highter, bool &lower)
{
int hh = 0, tt = 0;
q[0] = {xx, yy};
st[xx][yy] = true;
while (hh <= tt)
{
auto t = q[hh++];
for (int i = t.first - 1; i <= t.first + 1; i++)
for (int j = t.second - 1; j <= t.second + 1; j++)
{
if (i < 1 || i > n || j < 1 || j > n)
continue;
if (g[i][j] != g[t.first][t.second])
{
if (g[i][j] > g[t.first][t.second])
highter = true;
else
lower = true;
}
else if (!st[i][j])
{
q[++tt] = {i, j};
st[i][j] = true;
}
}
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> g[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
if (st[i][j])
continue;
bool higher = false, lower = false;
bfs(i, j, higher, lower);
if (!higher)
res_n++;
if (!lower)
res_u++;
}
cout << res_n << " " << res_u << endl;
return 0;
}