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;
}