题目描述:给定一个地图,为小朋友想要旅行的区域,地图被分为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为山峰(山谷)当且仅当:
1.S的所有格子都有相同的高度。
2.S的所有格子都联通3.对于s属于S,与s相邻的s’不属于S。都有ws > ws’(山峰),或者ws < ws’(山谷)。
你的任务是,对于给定的地图,求出山峰和山谷的数量,如果所有格子都有相同的高度,那么整个地图即是山峰,又是山谷。
输入 第一行包含一个正整数n,表示地图的大小(1<=n<=1000)。接下来一个n*n的矩阵,表示地图上每个格子的高度。(0<=w<=1000000000)
输出 应包含两个数,分别表示山峰和山谷的数量。
原题链接:[POI2007] GRZ-Ridges and Valleys - 洛谷
样例1:
输入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:
输入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
代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e3+10;
typedef pair<int,int> PII;
PII q[N*N];
int g[N][N];
bool st[N][N];
int n,ans;
int dx[]={-1,-1,-1,0,1,1,1,0},dy[]={-1,0,1,1,1,0,-1,-1};
void bfs(int x,int y,bool& high,bool& low)
{
int hh=0,tt=0;
//入队
q[0]={x,y};
st[x][y]=true;
while(hh<=tt)
{
auto t=q[hh++];
for(int i=0;i<8;i++)
{
int a=t.first+dx[i],b=t.second+dy[i];
if(a<0||a>=n||b<0||b>=n)continue;
if(g[a][b]!=g[t.first][t.second])
{
if(g[t.first][t.second]>g[a][b])
low=true;
else
high=true;
}
else if(!st[a][b])
{
q[++tt]={a,b};
st[a][b]=true;
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
scanf("%d",&g[i][j]);
}
int ans1=0,ans2=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(!st[i][j])
{
bool high=false,low=false;
bfs(i,j,high,low);
if(!high)
ans1++;
if(!low)
ans2++;
}
}
}
printf("%d %d",ans1,ans2);
return 0;
}
代码分析:
这段 C++ 代码是用来分析一个给定的 n x n
网格(g
),找出所有的“高点”和“低点”。高点是指没有比它高的邻居的区域,低点是指没有比它低的邻居的区域。代码使用了宽度优先搜索(Breadth-First Search, BFS)算法来实现这个任务。我们一步步来分析这段代码。
const int N=1e3+10;
typedef pair<int,int> PII;
PII q[N*N];
int g[N][N];
bool st[N][N];
int n,ans;
int dx[]={-1,-1,-1,0,1,1,1,0},dy[]={-1,0,1,1,1,0,-1,-1};
N
: 定义了网格的最大大小,这里设为 1010。PII
: 使用typedef
定义了一个新类型,代表一个坐标对,用于存储 BFS 队列中的元素。q
: 这是一个队列,用于存储 BFS 过程中需要访问的网格坐标。g
: 一个二维数组,存储网格中每个单元格的值(高度)。st
: 一个二维布尔数组,标记网格中的单元格是否已经被访问过。n
: 网格的实际大小。dx
和dy
: 这两个数组用于移动到一个单元格的八个可能的相邻位置。
bfs
函数的实现:
void bfs(int x,int y,bool& high,bool& low)
{
int hh=0,tt=0;
q[0]={x,y};
st[x][y]=true;
while(hh<=tt)
{
auto t=q[hh++];
for(int i=0;i<8;i++)
{
int a=t.first+dx[i],b=t.second+dy[i];
if(a<0||a>=n||b<0||b>=n)continue;
if(g[a][b]!=g[t.first][t.second])
{
if(g[t.first][t.second]>g[a][b])
low=true;
else
high=true;
}
else if(!st[a][b])
{
q[++tt]={a,b};
st[a][b]=true;
}
}
}
}
- 函数
bfs
接收四个参数:x
和y
表示起始单元格的坐标,high
和low
是引用布尔值,用于记录是否存在比当前单元格高或低的相邻单元格。 hh
和tt
是队列的头和尾指针。q[0]={x,y};
将起始单元格入队,并将其标记为已访问。while
循环会一直运行,直到队列为空。在循环内部,它会检查当前单元格的所有相邻单元格。- 如果相邻单元格的值不同于当前单元格,并且相邻单元格的值更高,则设置
high
为true
;如果相邻单元格的值更低,则设置low
为true
。 - 如果相邻单元格的值相同,并且该单元格尚未被访问过,则将它加入队列并标记为已访问。
main
函数的实现:
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
scanf("%d",&g[i][j]);
}
int ans1=0,ans2=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(!st[i][j])
{
bool high=false,low=false;
bfs(i,j,high,low);
if(!high)
ans1++;
if(!low)
ans2++;
}
}
}
printf("%d %d",ans1,ans2);
return 0;
}
main
函数首先通过scanf
读取网格的大小n
,然后读取网格中每个单元格的值填充到g
数组。- 定义了两个计数器
ans1
和ans2
,分别用于计算高点和低点的数量。 - 使用两层嵌套的
for
循环遍历整个网格。对于每个尚未访问过的单元格,调用bfs
函数。 - 如果
bfs
函数执行后,high
为false
,则ans1
计数器增加,表示找到一个高点。如果low
为false
,则ans2
计数器增加,表示找到一个低点。 - 最后,使用
printf
输出高点和低点的总数。
总的来说,这段代码通过 BFS 算法来遍历网格,检查每个单元格是否是高点或低点,并计算它们的总数。
详细分析样例2:
我们想要分析的是,对于每一个未被访问过的单元格,通过 BFS 算法来判断它是否是一个高点或低点。
程序执行的流程大致如下:
-
初始化
ans1
和ans2
为 0,它们分别用来记录高点和低点的数量。 -
外层循环遍历网格的每一行,内层循环遍历网格的每一列。
-
对于每个单元格
(i, j)
,如果st[i][j]
为false
(意味着该单元格尚未被访问),则执行bfs(i, j, high, low)
。 -
在
bfs
函数中,我们将检查该单元格的所有相邻单元格。如果存在一个相邻单元格的高度大于当前单元格,则high
被设置为true
;如果存在一个相邻单元格的高度小于当前单元格,则low
被设置为true
。 -
一旦所有相邻单元格都被考虑过后,
bfs
函数返回到main
函数。此时,如果high
仍然为false
,则说明当前的单元格没有比它高的相邻单元格,它是一个高点,ans1
自增1。同理,如果low
为false
,则说明当前单元格没有比它低的相邻单元格,它是一个低点,ans2
自增1。
现在我们根据这个流程来手动分析给定的网格:
- 单元格 (0,0) 是
5
。它有一个高邻居7
(在 (0,1)) 和一个低邻居5
(在 (1,0))。所以它既不是高点也不是低点。 - 单元格 (0,1) 是
7
。它有高邻居8
(在 (0,2)) 和低邻居5
(在 (0,0) 和 (1,1))。所以它既不是高点也不是低点。 - 单元格 (0,2) 是
8
。它没有高邻居,但有低邻居7
。所以它是一个高点。ans1
加 1。 - 单元格 (0,3) 是
3
。它有高邻居8
(在 (0,2)) 和低邻居2
(在 (3,3))。所以它既不是高点也不是低点。 - 单元格 (0,4) 是
1
。它有高邻居3
(在 (0,3)) 但没有低邻居。所以它是一个低点。ans2
加 1。
以此类推,遍历整个网格。最终,我们可以计算出所有的高点和低点。在这个特定的例子中,根据程序的逻辑,我们得到的结果是:
- 高点 (
ans1
) 的数量:3个。 - 低点 (
ans2
) 的数量:3个。
这个结果是通过手动模拟算法过程得出的。如果你执行实际的 C++ 程序,应该也会得到相同的结果。
大家可以练习相似例题:OpenJudge - 1817:城堡问题