题目+提交地址
(这个题调了两个小时多,我是个蒟蒻QAQ)
题意:(正文题目的意思有点没有说明白,应该是8个方向,刚开始时我以为是四个方向,一直WA)找有多少个山峰和山谷,两个答案中间是空格隔开(因为这一点不注意会WA掉全部答案)
做题经历:刚开始直接用深搜做,发现在函数里面return的时候不方便,又改成了广搜,做着做着发现深搜也可以做的,因为每次搜索的过程中需要标记,而每次标记的都是相同的元素。因为刚开始想的是搜索两次,一次山峰,一次山谷,但是由于中途要为第二次搜索保存现场,所以要用到mecpy函数,同时两次搜索也会耗费很多时间,结果TLE(意料之中)。后来想到:其实可以同时进行,立两个flag,函数结束时不可能两个flag都没变,这时只需要更新对应的结果就OK了
思路:先记录所有点的x,y,和权值w;然后遍历,如果这个点没有被标记,那么它所连接着的连通块也没有被查询过,就用queue来储存新的点,不断地向下查找八个方向,同时查找联通块的边缘。两种情况:1边缘是不合法的地区,那直接continue;2边缘比该点大(该连通块是山谷时满足条件),边缘比该点小(该连通块是山峰时满足条件);如果不满足条件,对应的flag赋值为0
TLE代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
#define de(x) cout<<x<<" ";
#define Pu puts("");
#define sf(x) scanf("%lld",&x);
#define int long long
#define x first
#define y second
#define P pair<int,int>
const int N=1e3+10;
int dx[]={0,0,1,-1,1,1,-1,-1};
int dy[]={1,-1,0,0,1,-1,1,-1};
int n;
int vis[N][N],val[N][N];
int tmp_vis[N][N];
P p[N*N];
queue<P>q;
bool valid(int x,int y){
if(x<0||y<0||x>=n||y>=n) return 0;
return 1;
}
bool bfs(int op){
int l,r,f=1;
P now;
while(q.size()){
now=q.front();q.pop();
for(int i=0;i<8;i++){
l=now.x+dx[i];
r=now.y+dy[i];
if(!valid(l,r)) continue;
if(f==1){//判断是否是山峰或者山谷
if(op==0&&val[now.x][now.y]>val[l][r]) f=0;
if(op==1&&val[now.x][now.y]<val[l][r]) f=0;//不 return :为了标记所有相同的点
}
if(vis[l][r]) continue;//判断能否继续向下,且只找一样的
//想着标记一部分,不影响第二次;但是会重复记录结果
// if(val[now.x][now.y]>val[l][r]&&op==1) vis[l][r]=1;
// if(val[now.x][now.y]<val[l][r]&&op==0) vis[l][r]=1;
// vis[l][r]=1;
if(val[now.x][now.y]==val[l][r]){//该点和下一个点值相同,继续往下:标记查找所有
q.push(make_pair(l,r));
vis[l][r]=1;
}
}
}
if(f!=0) return 1;
return 0;//该连通块上的所有点都被标记,且不是山峰或者山谷
}
signed main(){
cin>>n;
int w,cnt=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
sf(w);
p[++cnt].x=i; p[cnt].y=j;
val[i][j]=w;
}
}
int an_d=0,an_g=0;
for(int i=1;i<=cnt;i++){
if(vis[p[i].x][p[i].y]) continue;
vis[p[i].x][p[i].y]=1;
memcpy(tmp_vis,vis,sizeof(vis));
q.push(p[i]);
if(bfs(0)) an_d++;
//恢复第一次查找前的现场,第二次不用
memcpy(vis,tmp_vis,sizeof(tmp_vis));
q.push(p[i]);
if(bfs(1)) an_g++;
}
printf("%lld %lld\n",an_g,an_d);
}
AC代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
#define de(x) cout<<x<<" ";
#define Pu puts("");
#define sf(x) scanf("%lld",&x);
#define int long long
#define x first
#define y second
#define P pair<int,int>
const int N=1e3+10;
int dx[]={0,0,1,-1,1,1,-1,-1};
int dy[]={1,-1,0,0,1,-1,1,-1};
int n;
int vis[N][N],val[N][N];
int tmp_vis[N][N];
int an_d=0,an_g=0;
P p[N*N];
queue<P>q;
bool valid(int x,int y){
if(x<0||y<0||x>=n||y>=n) return 0;
return 1;
}
void bfs(){
int l,r,f_g=1,f_d=1;
P now;
while(q.size()){
now=q.front();q.pop();
for(int i=0;i<8;i++){
l=now.x+dx[i];
r=now.y+dy[i];
if(!valid(l,r)) continue;
if(f_g==1||f_d==1){//判断是否是山峰或者山谷
if(val[now.x][now.y]>val[l][r]) f_d=0;
if(val[now.x][now.y]<val[l][r]) f_g=0;//不 return :为了标记所有相同的点
}
if(vis[l][r]) continue;//判断能否继续向下,且只找一样的
//想着标记一部分,不影响第二次;但是会重复记录结果
// if(val[now.x][now.y]>val[l][r]&&op==1) vis[l][r]=1;
// if(val[now.x][now.y]<val[l][r]&&op==0) vis[l][r]=1;
// vis[l][r]=1;
if(val[now.x][now.y]==val[l][r]){//该点和下一个点值相同,继续往下:标记查找所有
q.push(make_pair(l,r));
vis[l][r]=1;
}
}
}
if(f_g!=0) an_g++;
if(f_d!=0) an_d++;//该连通块上的所有点都被标记,且不是山峰或者山谷
}
signed main(){
cin>>n;
int w,cnt=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
sf(w);
p[++cnt].x=i; p[cnt].y=j;
val[i][j]=w;
}
}
for(int i=1;i<=cnt;i++){
if(vis[p[i].x][p[i].y]) continue;
vis[p[i].x][p[i].y]=1;
// memcpy(tmp_vis,vis,sizeof(vis));
// q.push(p[i]);
// if(bfs(0)) an_d++;
// //恢复第一次查找前的现场,第二次不用
// memcpy(vis,tmp_vis,sizeof(tmp_vis));
// q.push(p[i]);
// if(bfs(1)) an_g++;
q.push(p[i]);
bfs();
}
printf("%lld %lld\n",an_g,an_d);
}