1428. B Baby Climber(心宝去爬山)
Constraints
Time Limit: 1 secs, Memory Limit: 32 MB
Description
心宝热爱运动,喜欢爬山。一天他去道场山游玩。杭嘉湖平原多丘陵,心宝手上有一幅地图,是分层设色地形图(地图为长宽均不超过50的矩阵),心宝想算算地图上有多少个山顶他需要翻越。
为了方便,地图上地势高度用小写字母'a' - 'z'表示,序号越靠前的字母表示地势越高。山顶的定义是指其东南西北四个相邻地点都没有更高的地势存在即为山顶,若有相邻的一片等高地势,则算做一个山顶,例如一幅地图如下表示:
ccccc
cbbbc
cbabc
cbbbc
ccccc
则显然的,地图中a点为山顶。整张地图只有一个山顶,而
cbbcbabc
cbbcbabc
cbbcbabc
cbbcbabc
显然图中有两条山脉分别是左边的b和右边的a,所以算两个山顶。
请你计算一下,心宝所给的地图中有几个山顶
Input
输入输出包括多个Case。 每个Case包含多行,每一行包含相等数量的小写字母,表示地图的每一行内容。 每个Case以*****表示结束。 数据间没有多余的空格和空行。 输入保证每个Case最多不超过50行,每行不超过50个字符。
Output
每个Case一行输出山顶数目
Sample Input
ccccc cbbbc cbabc cbbbc ccccc ***** cbbcbabc cbbcbabc cbbcbabc cbbcbabc *****
Sample Output
1 2
Problem Source
<Good Luck, Sysu Teams>,Renaissance
下面是有两份代码,第一份是网上找到的优秀代码,第二份是我自己的代码。
代码一的思路:运用优先队列,依次pop队列中代表高度最高的点,然后向四邻域扩散,将低于该点的点的标志位置0,然后再将被置0的点进行四邻域扩散,更低的点置零。
这样一个循环下来,就能找到一个山峰,同时也能排除许多不可能的点。
优点:(1)代码简练
(2)利用了优先队列从最高点开始循环,而不是墨守的用for从头开始循环。
</pre><pre name="code" class="cpp"><span style="font-family:SimSun;font-size:14px;">// Problem#: 1428
</span><pre><span style="font-family:SimSun;font-size:14px;">// Submission#: 2196979
// The source code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
// URI: http://creativecommons.org/licenses/by-nc-sa/3.0/
// All Copyright reserved by Informatic Lab of Sun Yat-sen University
#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
char map[51][51];
int d[][2]={0,1,0,-1,1,0,-1,0};
struct node{
char c;
int x,y;
friend bool operator<(const node& a,const node& b){
return a.c>b.c;
}
};
int main(){
while (gets(map[0]))
{
int n=1,i,j,m=strlen(map[0]);
std::priority_queue<node> qu;
node u,v;
while (gets(map[n]) && map[n][0]!='*')
n++;
for (i=0; i<n; ++i) for (j=0; j<m; ++j)
{
u.c=map[i][j];
u.x=i; u.y=j;
qu.push(u);
}
int ans=0;
while (!qu.empty())
{
u=qu.top();
qu.pop();
if (!map[u.x][u.y])
continue;
ans++;
std::queue<node> q;
q.push(u);
while (!q.empty())
{
u=q.front();
q.pop();
for (i=0; i<4; ++i)
{
v.x=u.x+d[i][0];
v.y=u.y+d[i][1];
if (v.x<0 || v.x>=n || v.y<0 || v.y>=m)
continue;
if (map[v.x][v.y] && map[v.x][v.y]>=u.c)
{
v.c=map[v.x][v.y];
map[v.x][v.y]=0;
q.push(v);
}
}
}
}//用了2个队列,一个优先队列和一个队列
printf("%d\n",ans);
}
return 0;
} </span>
代码二的思路:如果四邻域有比它高的,就返回false。
否则用for循环对每一个点进行判断,如果它的四邻域没有比它高的,置这个点的标志位为1(代表已经判断过了),然后取它四邻域中和它等高的点进行迭代,同时置这个等高点的标志位为1。
如果最终输出true就代表这是一个峰。
感想:这段代码在sicily上通不过,提示wrong answer,上一段代码则通过了。用随机产生的50*50阵对两份代码进行试验,结果一样。
速度测试:第二段代码速度超过第一段。
#include <iostream> #include <string> #include <cstdio> #include <cmath> using namespace std; char s[51][51]; int a[51][51][5]={0}; //记录上下左右的值和自己的差值,a[x][y][0]代表(x,y)是否已经被用过 int n,m; int dx[5]={0,-1,1,0,0}; int dy[5]={0,0,0,-1,1}; void init() { n=0;int i,j,z; //n行 for(i=0;i<51;i++) for(j=0;j<51;j++) for(z=0;z<5;z++) a[i][j][z]=0; m=strlen(s[0]);//m列 while(s[n][0]!='*') { if(n!=0) { for(i=0;i<m;i++)//确定n的上和n-1的下的差值 { a[n][i][1]=-1*(s[n][i]-s[n-1][i]); a[n-1][i][2]=-1*(s[n-1][i]-s[n][i]); } } else for(i=0;i<m;i++)//把第一行的上的差值置为0 a[0][i][1]=0; for(i=1;i<m-1;i++)//确定左右的差值 { a[n][i][3]=-1*(s[n][i]-s[n][i-1]); a[n][i][4]=-1*(s[n][i]-s[n][i+1]); } a[n][0][3]=0; a[n][0][4]=-1*(s[n][0]-s[n][1]); a[n][m-1][3]=-1*(s[n][m-1]-s[n][m-2]); a[n][m-1][4]=0; n++; gets(s[n]); } for(i=0;i<m;i++) //把最后一行的下的差值置为0 a[n][i][2]=0; } int judge(int x,int y) { bool b=true;int i; for(i=1;i<5;i++) if(a[x][y][i]<0) return 0; a[x][y][0]=1; //要是上下左右的差值都大于等于0,那次这个点经过这次判断后下次不需要判断,置1 for(i=1;i<5;i++) { int u=x+dx[i]; int v=y+dy[i]; if(u<0||u>=n||v>=m||v<0)//保证(u,v)不超出范围 continue; if(a[x][y][i]==0&&a[u][v][0]==0) //a[u][v][0]==0保证不会重复检测周围的等高点 b=b*judge(u,v); } return b; } int get_top() { int count=0;int i,j; for(i=0;i<n;i++) for(j=0;j<m;j++) { if(a[i][j][0]==1) continue; else if(judge(i,j)) count++; } return count; } int main() { while(gets(s[0])) { init(); cout<<get_top()<<endl; } return 0; }