广度优先搜索BFS-P3456 [POI2007]GRZ-Ridges and Valleys

题目+提交地址
(这个题调了两个小时多,我是个蒟蒻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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值