看一下今天的内容
目录
bfs简述:
广度优先算法(Breadth-First-Search),简称BFS。从知识点看属于图结构的搜索算法,是一种相对容易理解的简单算法。
BFS算法从问题的初始状态(起点)出发,根据状态转换规则(图结构中的边),遍历所有可能的状态(其他节点),直到找到终结状态(终点)。因此BFS算法的复杂度和状态集合的总数密切相关。
BFS算法虽然出自图结构,但其常用的领域却不是解决图论相关问题。一些常见的问题形式如(1)走迷宫最短路径(2)数字按规则转换的最少次数(3)棋盘上某个棋子N步后能到达的位置总数(4)病毒扩散计算(5)图像中连通块的计算。小结:BFS算法常用于求最短的步数或者求扩散性质的区域问题。
上代码:
//bfs(广搜模板)
q.push(初始状态);
while(!q.empty()){
tmp =q.front(); q.pop();
for(枚举所有情况v){
if(情况合法并去重){
标记操作; q.push(v);
}
}
}
好了,学会了这种思想后就直接上题了。
题目:奇怪的电梯
思路:
抓关键词:至少按几次,相当于至少走多少步,bfs错不了。
从最开始状态不断让后继入队即可,直到整个队空为止。
#include<bits/stdc++.h> //(广度优先)(有点像图的基本遍历)
using namespace std;
int n,a,b,to[205];
bool vis[205]; //标记哪个楼层到过,防止再到一次(用于枝剪)
struct node{
int id,step; //id表示楼层号,step表示按钮次数
}x;
int main()
{
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;i++) scanf("%d",&to[i]);
queue<node> q;
q.push((node){a,0});
while(q.size())
{
x=q.front();q.pop(); //保存
if(x.id==b) break;
if(x.id+to[x.id]<=n&&!vis[x.id+to[x.id]]) //防越界,枝剪:来过就不用来了
{
q.push((node){x.id+to[x.id],x.step+1}); //标记,入队
vis[x.id+to[x.id]]=1; //标记来过
}
if(x.id-to[x.id]>=1&&!vis[x.id-to[x.id]])//写的丑了点,不会入队两次的放心吧,有判重呢
{
q.push((node){x.id-to[x.id],x.step+1});
vis[x.id-to[x.id]]=1;
}
}
if(x.id==b) printf("%d",x.step);
else printf("-1"); //bfs只要不是break出来的的,一定根本就到不了
return 0;
}
再来一题吧
题目:马的遍历
思路 :
理解马的走路方式,然后设置转移数组即可。
然后f[i][j]表示到这个点所需要的步数。不断维护这个数组即可
#include <bits/stdc++.h> //马的遍历 (广度优先bfs)
using namespace std;
struct point{ //坐标
int x,y;
};
int main(){
int n,m,x0,y0,f[404][404],dx[8]={-1,-2,-2,-1,1,2,2,1},dy[8]={-2,-1,1,2,2,1,-1,-2}; //动态转移数组
queue<point> q;
memset(f,-1,sizeof(f));
cin>>n>>m>>x0>>y0;
f[x0][y0]=0;
point tp={x0,y0},p; //tp是起始点,p是下一步的随机点
q.push(tp);//队列起始状态
while(!q.empty()){
tp=q.front(); q.pop(); //暂存,弹出
for(int i=0;i<8;i++){
int x=dx[i]+tp.x,y=dy[i]+tp.y; //(x,y)是下一步的坐标
if(x<1||x>n||y<1||y>m||f[x][y]!=-1) continue;
f[x][y]=f[tp.x][tp.y]+1; //标记
p={x,y}; q.push(p); //入队
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cout<<f[i][j]<<" ";
}
cout<<'\n';
}
return 0;
}
最后一题
题目:流星坠落
思路:
如果当流星按时间顺序一秒一秒的落下很不现实,时间复杂度太大了。还不如先打表,再搜索呢
其实完全可以让流星全部降落,只需要记录下每个点烧焦的最早时间,未设置则表示这个点永远不会被烧到。然后让我们的主角开始bfs跑图,走那些目前还安全的地方,如果能走到永远安全的地方就直接结束bfs即可。否则,当我们bfs全部跑完的时候即表示根本找不到这样的位置!
#include <bits/stdc++.h>//P2895 (bfs广度优先)
using namespace std;
struct point{
int x,y,t,step,vis;//(x,y)是坐标,t是流星的坠落时间,step是贝茜当前步数也是当前时间数
}ps[1005][1005];
int main(){
int m,x,y,t,tx,ty,dx[5]={-1,1,0,0},dy[5]={0,0,-1,1};//转移数组
cin>>m;
memset(ps,-1,sizeof(ps));
for(int i=0;i<1000;i++) //ps结构体的x,y初始化
for(int j=0;j<1000;j++){
ps[i][j].x=i; ps[i][j].y=j;
}
for(int i=0;i<m;i++){ //ps结构体的t初始化(表示t时间后烧毁)
cin>>x>>y>>t;
for(int j=0;j<5;j++){//周围都要烧焦
tx=x+dx[j]; ty=y+dy[j];
if(tx<0||ty<0) continue; //防止数组越界
if(ps[tx][ty].t==-1||ps[tx][ty].t>t) ps[tx][ty].t=t;//原位置没有被占或原位置早就被烧毁,时间被更新
}
}
queue<point> q;
ps[0][0].vis=1; ps[0][0].step=0;//step就是记录时间
q.push(ps[0][0]); //队列初始化
while(!q.empty()){
point p=q.front(); q.pop(); //暂存,出队
for(int i=0;i<4;i++){
tx=p.x+dx[i]; ty=p.y+dy[i];
if(tx<0||ty<0||ps[tx][ty].vis==1) continue; //防止越界,防止重复访问(因为再回去或再停留原地已经没必要,时间已经变晚了)
if(ps[tx][ty].t==-1){ //t为-1的位置是永不被烧的位置,就是到了安全位置
cout<<p.step+1;
return 0;
}
if(p.step+1<ps[tx][ty].t){//到达烧毁之前位置
ps[tx][ty].vis=1;//标记
ps[tx][ty].step=p.step+1;//操作
q.push(ps[tx][ty]);//入队
}
}
}
cout<<-1;
return 0;
}