广度优先搜索(Breadth First Search, BFS)类似树的按层次遍历。
原则是:尽可能地先横向搜索;特点是:先访问的顶点其邻接点亦先被访问。
①从顶点v出发,置visited[v]=true,将v入队;
②只要队列不为空,重复将队头u出队,检查u的所有邻接点w,如果visited[u]=false,则将其入队并置visited[u]=true
P1443 马的遍历
来看题目:
题目描述
有一个n×m的棋盘(1<n,m<=400),在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步
输入格式
一行四个数据,棋盘的大小和马的坐标
输出格式
一个n×m的矩阵,代表马到达某个点最少要走几步(左对齐,宽5格,不能到达则输出-1)
看到最短路,首选BFS
因为DFS是一路走到黑,直到走不了再后退一步,一般用来灌水或回溯qwq
注意
①x和y都不要超过边界400
②二维数组的写法(我吐了,visited[x,y]是什么鬼,我居然写出这种东东,免不了某人一番嘲笑hhhhh)
③queue里面可以是一对数值,写法上就是pair
④n行m列,循环输出的时候,内层是到m为止,外层是到n为止(这都能忘我也是佩服自己)
⑤每次注意标明是否来过(visited数组)
#include <bits/stdc++.h>
using namespace std;
queue<pair<int,int> > q;
int step[500][500];
bool visited[500][500];
const int x_gain[] ={-1,-2,-2,-1,1,2,2,1};//用来存放x方向的位移
const int y_gain[] ={2,1,-1,-2,-2,-1,1,2};//用来存放y方向的位移
int main(){
memset(step,-1,sizeof(step));//注意memset用法
memset(visited,false,sizeof(visited));
int n,m,x,y;
cin>>n>>m>>x>>y;
q.push(make_pair(x,y));//注意push是一对
step[x][y]=0;
visited[x][y]=true;
while(!q.empty()){
int x_now=q.front().first;
int y_now=q.front().second;
q.pop();
for(int i=0;i<8;i++){
int xx=x_now+x_gain[i];
int yy=y_now+y_gain[i];
if(xx<1||xx>n||yy<1||yy>m||visited[xx][yy]) continue;
visited[xx][yy]=true;
step[xx][yy]=step[x_now][y_now]+1;
q.push(make_pair(xx,yy));
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
printf("%d ",step[i][j]);
}
cout<<endl;
}
return 0;
}
P1747 好奇怪的游戏
这题基本和上题一致,就不过多说明了。
传送门
题目描述
爱与愁大神坐在公交车上无聊,于是玩起了手机。一款奇怪的游戏进入了爱与愁大神的眼帘:***(游戏名被打上了马赛克)。这个游戏类似象棋,但是只有黑白马各一匹,在点x1,y1和x2,y2上。它们得从点x1,y1和x2,y2走到1,1。这个游戏与普通象棋不同的地方是:马可以走“日”,也可以像象走“田”。现在爱与愁大神想知道两匹马到1,1的最少步数,你能帮他解决这个问题么?
输入格式
第1行:两个整数x1,y1
第2行:两个整数x2,y2
输出格式
第1行:黑马到1,1的步数
第2行:白马到1,1的步数
马既走“日”又走“田”,也无外乎这12种走法:
其他基本和上题一致,除了退出条件有变化,当x == 1&&y==1的时候输出step[1][1]
#include <bits/stdc++.h>
using namespace std;
queue<pair<int,int> > q;
int step[21][21];
bool visited[21][21];
const int x_gain[] ={-1,-2,-2,-1,1,2,2,1,-2,-2,2,2};//用来存放x方向的位移
const int y_gain[] ={2,1,-1,-2,-2,-1,1,2,2,-2,2,-2};//用来存放y方向的位移
int bfs(int x,int y){
memset(step,-1,sizeof(step));//注意memset用法
memset(visited,false,sizeof(visited));
while(!q.empty()) q.pop();//清空队列
q.push(make_pair(x,y));//注意push是一对
step[x][y]=0;
visited[x][y]=true;
while(!q.empty()){
int x_now=q.front().first;
int y_now=q.front().second;
q.pop();
if(x_now==1&&y_now==1) return step[1][1];
for(int i=0;i<12;i++){
int xx=x_now+x_gain[i];
int yy=y_now+y_gain[i];
if(xx<1||xx>20||yy<1||yy>20||visited[xx][yy]) continue;
visited[xx][yy]=true;
step[xx][yy]=step[x_now][y_now]+1;
q.push(make_pair(xx,yy));
}
}
}
int main(){
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
cout<<bfs(x1,y1)<<endl;
cout<<bfs(x2,y2)<<endl;
return 0;
}
P1746 离开中山路
可以说是一个模板套三道题目了。这题,和前两题都差不多。
唯一的亮点我觉得在于:怎么读一位数字。巧妙运用char只有一位的特点,声明char数组,然后用它来读,或者用scanf控制位数:
scanf("%1d",&mapp[c][d]);
美汁汁儿~~
题目描述
爱与愁大神买完东西后,打算坐车离开中山路。现在爱与愁大神在x1,y1处,车站在x2,y2处。现在给出一个n×n(n<=1000)的地图,0表示马路,1表示店铺(不能从店铺穿过),爱与愁大神只能垂直或水平着在马路上行进。爱与愁大神为了节省时间,他要求最短到达目的地距离(a[i][j]距离为1)。你能帮他解决吗?
输入格式
第1行:一个数 n
第2行~第n+1行:整个地图描述(0表示马路,1表示店铺,注意两个数之间没有空格)
第n+2行:四个数 x1,y1,x2,y2
输出格式
只有1行:最短到达目的地距离
#include <bits/stdc++.h>
using namespace std;
#define maxn 1010
queue<pair<int,int> > q;
int step[maxn][maxn];
bool visited[maxn][maxn];
char Map[maxn][maxn];//亮点所在
int n;
const int x_gain[] ={-1,0,1,0};//用来存放x方向的位移
const int y_gain[] ={0,-1,0,1};//用来存放y方向的位移
int bfs(int x,int y,int d_x,int d_y){//d_x是destination的横坐标,d_y是destination的纵坐标
memset(step,-1,sizeof(step));//注意memset用法
memset(visited,false,sizeof(visited));
while(!q.empty()) q.pop();//清空队列
q.push(make_pair(x,y));//注意push是一对
step[x][y]=0;
visited[x][y]=true;
while(!q.empty()){
int x_now=q.front().first;
int y_now=q.front().second;
q.pop();
if(x_now==d_x&&y_now==d_y) return step[d_x][d_y];
for(int i=0;i<4;i++){
int xx=x_now+x_gain[i];
int yy=y_now+y_gain[i];
if(xx<1||xx>n||yy<1||yy>n||visited[xx][yy]||Map[xx][yy]=='1') continue;//=='1'这个判断太妙了!
visited[xx][yy]=true;
step[xx][yy]=step[x_now][y_now]+1;
q.push(make_pair(xx,yy));
}
}
}
int main(){
int x1,y1,x2,y2;
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) cin>>Map[i][j];
cin>>x1>>y1>>x2>>y2;
cout<<bfs(x1,y1,x2,y2)<<endl;
return 0;
}
P2298 Mzc和男家丁的游戏
题目背景
mzc与djn的第二弹。
题目描述
mzc家很有钱(开玩笑),他家有n个男家丁(做过上一弹的都知道)。他把她们召集在了一起,他们决定玩捉迷藏。现在mzc要来寻找他的男家丁,大家一起来帮忙啊!
由于男家丁数目不多,再加上mzc大大的找人【laopo】水平很好,所以一次只需要找一个男家丁。
输入格式
第一行有两个数n,m,表示有n行m列供男家丁躲藏,
之后n行m列的矩阵,‘m‘表示mzc,‘d’表示男家丁,‘#’表示不能走,‘.‘表示空地。
输出格式
一行,若有解:一个数sum,表示找到男家丁的最短移动次数。
若无解:输出“No Way!”。
这题唯一要注意的是无解要注意输出No Way!
然后最开始要找到主人的位置,就行。
#include <bits/stdc++.h>
using namespace std;
#define maxn 2010
queue<pair<int,int> > q;
int step[maxn][maxn];
bool visited[maxn][maxn];
char Map[maxn][maxn];
int n,m;
const int x_gain[] ={-1,0,1,0};//用来存放x方向的位移
const int y_gain[] ={0,-1,0,1};//用来存放y方向的位移
int bfs(int x,int y){
memset(step,-1,sizeof(step));//注意memset用法
memset(visited,false,sizeof(visited));
while(!q.empty()) q.pop();//清空队列
q.push(make_pair(x,y));//注意push是一对
step[x][y]=0;
visited[x][y]=true;
while(!q.empty()){
int x_now=q.front().first;
int y_now=q.front().second;
q.pop();
if(Map[x_now][y_now]=='m') return step[x_now][y_now];
for(int i=0;i<4;i++){
int xx=x_now+x_gain[i];
int yy=y_now+y_gain[i];
if(xx<1||xx>n||yy<1||yy>m||visited[xx][yy]||Map[xx][yy]=='#') continue;
visited[xx][yy]=true;
step[xx][yy]=step[x_now][y_now]+1;
q.push(make_pair(xx,yy));
}
}
return -1;
}
int main(){
int start_x,start_y;
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cin>>Map[i][j];
if(Map[i][j]=='d'){
start_x=i;
start_y=j;
}
}
int res = bfs(start_x,start_y);
if(res==-1) cout<<"No Way!"<<endl;
else cout<<res<<endl;
return 0;
}
做了这么多题目了,该总结模板了,虾面就是广搜的模板:
int bfs(int x,int y)
{
q.push(make_pair(x,y)); //起点加入队列
vis[x][y]=true; //标记
while(!q.empty()) {
xx=q.front().first;
yy=q.front().second; //获取起始坐标
q.pop(); //弹出队列
if(符合条件) return ans(答案);
for(int i=0;i<走法;i++)
{
tx=xx+dx[i];
ty=yy+dy[i];
if(符合条件) continue;
if(符合条件) continue; //符合条件跳过循环
/*
可行,执行该部分语句
*/
q.push(make_pair(tx,ty)); //加入队列
}
}
}