算法day5 bfs搜索
一 马的遍历
首先我们之前学习bfs的时候是以走迷宫来讲述的,但是那个是一步一步走的,如果我们不是一步一步走的,而是有规律性,但是又不是一步一步走的该怎么办?
首先我们分析一下走日子的话不就是8个方向吗
然后我们只需要在向量数组里面写8个方向的参数就好了,但是怎么写呢?
我们来开这个图,每一个绿色的点就是它所对应的单位向量的坐标,所以我们就可以写出这个数组
int dx[]={2, 1,-1,-2,-2,-1,1,2}; int dy[]={-1,-2,-2,-1, 1, 2,2,1};
这个就是对应每一个点的向量坐标
后面呢就是跟走迷宫一样的操作了#include<iostream> #include<cstring> #include<algorithm> using namespace std; typedef pair<int,int> PII; const int N = 410; int n,m; int dist[N][N]; PII q[N*N]; int hh =0,tt=-1; int dx[]={2, 1,-1,-2,-2,-1,1,2}; int dy[]={-1,-2,-2,-1, 1, 2,2,1}; void bfs(int x,int y){ dist[x][y]=0; q[++tt]={x,y}; while(hh<=tt){ auto t = q[hh++]; for(int i=0;i<8;i++){ int a = t.first + dx[i]; int b = t.second + dy[i]; if(a>n||a<1||b>m||b<1) continue; if(dist[a][b]>=0) continue; dist[a][b]=dist[t.first][t.second]+1; q[++tt]={a,b}; } } } int main(){ scanf("%d %d",&n,&m); int x,y; scanf("%d %d",&x,&y); memset(dist,-1,sizeof(dist)); bfs(x,y); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ printf("%-5d",dist[i][j]); } printf("\n"); } return 0; }
这里比较难以理解的就是这个队列了
当然你也可以用stl库里面的队列,但是那个运行起来相对较为缓慢,我们为了提高效率就手写了一个队列
我们来分析一下这个队列是怎么写的PII q[N*N]; int hh =0,tt=-1;
首先这个队列就是先进先出嘛
然后我就定义了一个数组,这个数组的类型是二元组
然后这个hh是这个队列的头部,这个tt是队列的尾部
为什么设置为-1呢?因为当有一个元素插入进来之后不就是tt变成了0了么
出去是hh动,进来是tt动
然后我们就是写队列dist[x][y]=0; q[++tt]={x,y}; while(hh<=tt){ auto t = q[hh++]; for(int i=0;i<8;i++){ int a = t.first + dx[i]; int b = t.second + dy[i]; if(a>n||a<1||b>m||b<1) continue; if(dist[a][b]>=0) continue; dist[a][b]=dist[t.first][t.second]+1; q[++tt]={a,b}; } }
首先这个++tt就是push的操作,表示把一个元素放入
还记得前缀表达式和后缀表达h式吧,这就是,先++然后把元素进行放入
这个hh<=tt就是表示这个队列不可以为空
然后后面的hh++就是先把这个元素取出,然后再pop这个hh到后面去了,就是pop操作了
然后下面就是在结尾处加一个元素
二 血色先锋队
我们来看这个题目,这个题目就是多源bfs了,就是多个地方同时走,但是这个也不影响我们解题,因为我们队列是先进先出的,然后就是上一个感染源出来之后,就是下一个感染源,如果这个时候有两个感染源的话就是下一步就是第一个感染源的分支先出了,这样我们就做到了每个感染源都有考虑到的方法
#include<iostream> #include<algorithm> #include<cstring> using namespace std; typedef pair<int,int> PII; const int N=510; int n,m; int dist[N][N]; PII q[N*N]; int hh=0,tt=-1; int dx[]={-1,0,1, 0}; int dy[]={ 0,1,0,-1}; void bfs(){ while(hh<=tt){ auto t = q[hh++]; for(int i=0;i<4;i++){ int a = t.first+ dx[i]; int b = t.second+dy[i]; if(a<1||a>n||b<1||b>m) continue; if(dist[a][b]>=0)continue; dist[a][b]=dist[t.first][t.second]+1; q[++tt]={a,b}; } } } int main(){ int a,b; scanf("%d %d",&n,&m); scanf("%d %d",&a,&b); memset(dist, -1 ,sizeof(dist)); while(a--){ int x,y; scanf("%d %d",&x,&y); dist[x][y]=0; q[++tt]={x,y}; } bfs(); while(b--){ int x,y; scanf("%d %d",&x,&y); printf("%d\n",dist[x][y]); } return 0; }
这个是代码,我是先输入感染源,判断完之后再输入首领,这样就可以直接打印了
总结
其实bfs都是有一个通用的模式就是
要一个向量坐标
而且要有条件判断,再每次走对应方向的时候
然后要有一个状态数组也可以叫做足迹数组,这样我们就可以每次去加自己的足迹值,如果题目要求要这个足迹值也可以打印出,又或者可以作为条件走没有走过的值
还要有一个边界条件,防止走出去了
最后就是队列了,我们一开始一定要放一个元素,然后取走它并且弹出,如果下一个值符合条件,我们就又可以下一个值放进去,然后就可以对这个子值进行判断
所以就是队列,足迹值,向量坐标,条件,continue的使用