洛谷BFS广度优先搜索 习题练习

本文详细介绍了BFS算法在六道洛谷编程题目中的应用,包括填涂颜色、条件迷宫问题、汽车拉力比赛中的路径搜索、双端队列广搜以及状态压缩。展示了如何通过BFS解决路径查找、迷宫遍历和最短路径等问题。
摘要由CSDN通过智能技术生成

BFS习题练习

1. 洛谷P1162 填涂颜色

——染色问题

#include<iostream>
#include<queue>
using namespace std;
const int N=35; 
int a[N][N],flag[N][N];
int n;
queue<pair<int,int>>q;
int fx[]={-1,0,0,1};
int fy[]={0,-1,1,0};

void bfs(int x,int y){
    q.push({x,y});
    flag[x][y]=1;
    while(q.size()){
        auto t=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            int nx=t.first+fx[i];
            int ny=t.second+fy[i];
            if(nx<0||nx>n+1||ny<0||ny>n+1) continue;
            if(a[nx][ny]==1) continue;
            if(flag[nx][ny]==1) continue;
            flag[nx][ny]=1;
            q.push({nx,ny});
        }
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
        }
    }
    bfs(0,0);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(a[i][j]==0&&flag[i][j]==0){
                a[i][j]=2;	// 直接就地改值
            }
            cout<<a[i][j]<<" ";
        }
        cout<<endl;
    }
    return 0;
}

2. 洛谷p2895 Meteor Shower S 天降陨石

——条件迷宫问题

#include<iostream>
#include<string.h> // memset头文件
#include<queue>
using namespace std;

const int N=305;
int boom[N][N],reachtime[N][N];
queue<pair<int,int> > q;
int m;
int fx[]={-1,0,0,1};
int fy[]={0,-1,1,0};

int bfs(){
    q.push({0,0});
    reachtime[0][0]=0;
    while(q.size()){
        auto t=q.front();
        q.pop(); 
        for(int i=0;i<4;i++){
            int nx=t.first+fx[i];
            int ny=t.second+fy[i];
            if(nx<0||ny<0) continue;	// 没有极大值判断,默认无限大场地
            if(reachtime[nx][ny]) continue;
            reachtime[nx][ny]=reachtime[t.first][t.second]+1;
            if(boom[nx][ny]<=reachtime[nx][ny]) continue;	// 小于等于
            q.push({nx,ny});
            if(boom[nx][ny]==0x3f3f3f3f) 	// 流星永远不能到达->安全~
                return reachtime[nx][ny];
        }
    }
    return -1;
}
int main(){
    cin>>m;
    memset(boom,0x3f,sizeof boom);
    for(int i=1;i<=m;i++){
        int x,y,t;
        cin>>x>>y>>t;
        boom[x][y]=min(boom[x][y],t);	// 可能有不止一颗流星坠落于相同地点
        for(int j=0;j<4;j++){
            int nx=x+fx[j];
            int ny=y+fy[j];
            if(nx<0||ny<0) continue;	// 只判断单边
            boom[nx][ny]=min(boom[nx][ny],t); // 同上上注释
        }
    }
    int ans=bfs();
    cout<<ans<<endl;
    return 0;
}

3. P2658 汽车拉力比赛

——BFS综合:二分+BFS

#include<iostream>
#include<queue>
#include<algorithm>
#include<string.h>
using namespace std;
const int N=505;
int a[N][N],isflag[N][N],st[N][N];
queue<pair<int,int> >q;
int n,m;
int searchbeginx,searchbeginy;	// 起始点
int flagcnt=0;
int fx[]={-1,0,0,1};
int fy[]={0,-1,1,0};

int check(int d){
    int covercnt=0;		// 当前走过路标数
    q.push({searchbeginx,searchbeginy});
    st[searchbeginx][searchbeginy]=1;
    covercnt++;
    while(q.size()){
        auto t=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            int nx=t.first+fx[i];
            int ny=t.second+fy[i];
            if(nx<1||nx>m||ny<1||ny>n) continue;
            if(st[nx][ny]) continue;
//注:下一句不能写成return,题目隐含条件为“可达”,只要存在一条路就行(刚开始没注意调了好长时间)
            if(abs(a[nx][ny]-a[t.first][t.second])>d) continue; 
            st[nx][ny]=1;
            q.push({nx,ny});
            if(isflag[nx][ny]){
                covercnt++;
                if(flagcnt==covercnt)	// 满足条件返回
                    return 1;
            }
        }
    }
    return 0;
}
int main(){
    cin>>m>>n;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
        }
    }
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            cin>>isflag[i][j];
            if(isflag[i][j]) flagcnt++;
        }
    }
    int havefound=0;	// 跳出外层循环flag
    for(int i=1;i<=m&&!havefound;i++){ // 条件放for循环里了~
        for(int j=1;j<=n;j++){
            if(isflag[i][j]){
                searchbeginx=i;
                searchbeginy=j;
                havefound=1;
                break;
            }
        }
    }
    int l=0,r=1e9,ans=0;
    while(l<=r){	// 常规二分
        int mid=(l+r)/2;
        memset(st,0,sizeof st);		// check一轮后重新初始化(全局变量格外注意这一点)
        while(q.size()) q.pop();	// 队列没clear()函数
        if(check(mid)){
            ans=mid;
            r=mid-1;
        }else
            l=mid+1;
    }
    cout<<ans<<endl;
    return 0;
}   

4. p4554小明的游戏

——双端队列广搜

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;

const int N=505;
char a[N][N];
int cost[N][N];
int n,m;
int x1,y1,x2,y2;
deque<pair<int,int> >q;
int fx[]={-1,0,0,1};
int fy[]={0,-1,1,0};

void bfs(int x1,int y1){
    q.push_back({x1,y1});
    cost[x1][y1]=0;
    while(q.size()){
        auto t=q.front();
        q.pop_front();
        for(int i=0;i<4;i++){
            int nx=t.first+fx[i];
            int ny=t.second+fy[i];
            if(nx<0||nx>=n||ny<0||ny>=m) continue;
            if(cost[nx][ny]!=-1) continue;
            if(a[nx][ny]==a[t.first][t.second]){
                cost[nx][ny]=cost[t.first][t.second];
                q.push_front({nx,ny});	// 相同为push_front(),保持单调性
            }else{
                cost[nx][ny]=cost[t.first][t.second]+1;
                q.push_back({nx,ny});	// 同queue常规push
            }
        }
    }
}
int main(){
    while(cin>>n>>m&&(m||n)){
        q.clear();
        memset(cost,-1,sizeof cost);	// 注:不能初始为0(此题0也可能已经被搜过)
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                cin>>a[i][j];
            }
        }
        cin>>x1>>y1>>x2>>y2;
        bfs(x1,y1);
        cout<<cost[x2][y2]<<endl;
    }
    return 0;
}

5. p1379 八数码难题

——状态压缩:一维&二维转换

#include<iostream>   
#include<queue>
#include<unordered_map>	
#include<string>
#include<algorithm>
using namespace std;

string start,endd="123804765";
queue<string>q;
unordered_map<string,int> dis;	// unordered_map使用
int ans=0;

int fx[]={-1,0,0,1};
int fy[]={0,-1,1,0};

int bfs(string s){
    q.push(s);
    dis[s]=0;
    while(q.size()){
        auto t=q.front();
        q.pop();
        if(t==endd) return dis[t];
        int nowdis=dis[t];	// 记录上一层距离
        int pos_0=t.find('0');
        int tx=pos_0/3,ty=pos_0%3;	// 一维索引转二维坐标
        for(int i=0;i<4;i++){
            int nx=tx+fx[i];
            int ny=ty+fy[i];
            if(nx<0||nx>=3||ny<0||ny>=3) continue;
            int pos_swap=nx*3+ny;	// 二维坐标转一维索引
            swap(t[pos_0],t[pos_swap]);
            if(!dis[t]){ // 注:别用continue情况判断,否则swap执行不到(找了好长时间)
                dis[t]=nowdis+1;
                q.push(t);
            }
            swap(t[pos_0],t[pos_swap]); //恢复现场
        }
    }
    return -1;
}
int main(){
    cin>>start;
    ans=bfs(start);
    cout<<ans<<endl;
    return 0;
}

6. 洛谷p1746 离开中山路

——双向广搜 优化版本

#include<iostream>   
#include<queue>
using namespace std;
const int N=1005;
int x1,y1,x2,y2,n,ans;
char a[N][N];
int st[N][N],dis[N][N];
queue<pair<int,int> >q;
int fx[]={-1,0,0,1};
int fy[]={0,-1,1,0};

int bfs(){
	// 双起点
    q.push({x1,y1}); st[x1][y1]=1; dis[x1][y1]=0;
    q.push({x2,y2}); st[x2][y2]=2; dis[x2][y2]=0;
    while(q.size()){
       auto t=q.front(); q.pop();	// 一个t就行,先正向 后反向
       for(int i=0;i<4;i++){
           int nx=t.first+fx[i];
           int ny=t.second+fy[i];
           if(nx<1||nx>n||ny<1||ny>n) continue;
           if(a[nx][ny]=='1') continue;
           if(st[t.first][t.second]+st[nx][ny]==3)	// 注意位置:(nx,ny)判断之前!
           		return dis[t.first][t.second]+dis[nx][ny]+1;
           if(dis[nx][ny]) continue;
           st[nx][ny]=st[t.first][t.second];	// 通用性
           q.push({nx,ny});
           dis[nx][ny]=dis[t.first][t.second]+1; // 更新dis
       }
    }
    return -1;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
        }
    }
    cin>>x1>>y1>>x2>>y2;
    ans=bfs();
    cout<<ans<<endl;
    return 0;
}
  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值