广度优先搜索
看个小例题,解救小哈。
#include<stdio.h>
struct note
{
int x;//横坐标
int y;//纵坐标
int f;
int s;//步数
};
int main()
{
struct note que[2501];//因为地图的大小不超过50*50,所以拓展不超过2500
int a[51][51]={0};//存储地图
int book[51][51]={0};//标记哪些点已在队列中,并初始化为0
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//不同方向
int head,tail;
int i,j,k,n,m,startx,starty,p,q,tx,ty,flag;
scanf("%d %d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
scanf("%d %d %d %d",&startx,&starty,&p,&q);
head=1;
tail=1;//将(1,1)加入队列表示已走过
que[tail].x=startx;
que[tail].y=starty;//往队列插入迷宫入口坐标
que[tail].f=0;
que[tail].s=0;
tail++;
book[startx][starty]=1;
flag=0;//用来标记是否达到目标点,0表示没有达到,1表示达到
while(head<tail)//当队列不为空时循环
{
for(k=0;k<4;k++)//枚举四个方向,计算下一点的坐标
{
tx=que[head].x+next[k][0];
ty=que[head].y+next[k][1];
if(tx<1||tx>n||ty<1||ty>m)
continue;//判断是否越界
if(a[tx][ty]==0&&book[tx][ty]==0)//判断是否时障碍物或者已在路径中
{
book[tx][ty]=1;//标记已走过,不需要把它还原
que[tail].x=tx;//插入新的点到队列中
que[tail].y=ty;
que[tail].f=head;
que[tail].s=que[head].s+1;
tail++;
}
if(tx==p&&ty==q)//判断是否到终点
{
flag=1;
break;
}
}
if(flag==1)
break;
head++;//一个点拓展结束后对后面的点继续拓展
}
printf("%d",que[tail-1].s);//tail是队列最后一位的下一位,所以要-1
getchar();getchar();
return 0;
}
完全copy书上的,没一点自己的思想,但是差不多懂了一点。然后找到了框架
void bfs() {
初始化,初始状态存入队列;
队列首指针head=0; 尾指针tail=1;
do {
指针head后移一位,指向待扩展结点;
for (int i=1;i<=max;++i) { //max为产生子结点的规则数
if (子结点符合条件) {
tail指针增1,把新结点存入列尾;
if (新结点与原已产生结点重复) 删去该结点(取消入队,tail减1);
else if (新结点是目标结点) 输出并退出;
}
}
} while(head < tail);//队列为空
}
自己尝试写了个题目
P1443 马的遍历
题目描述
有一个 n×m 的棋盘,在某个点 (x, y)上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。
输入格式
输入只有一行四个整数,分别为 n, m, x, y。
输出格式
一个 n×m 的矩阵,代表马到达某个点最少要走几步(不能到达则输出 -1)。
输入输出样例
输入 #1
3 3 1 1
输出 #1
0 3 2
3 -1 1
2 1 4
说明/提示
数据规模与约定
对于全部的测试点,保证400≤x≤n≤400,400≤y≤m≤400。
第一次写的代码,想要一个点一个点去搜索,虽然答案正确,但是时间超限了。
#include<stdio.h>
struct note
{
int p;//横坐标
int q;//纵坐标
int s;//步数
}que[400000];
int n,m,x,y,k;
int next[8][2]={{-1,2},{-2,1},{-2,-1},{-1,-2},{1,2},{2,1},{2,-1},{1,-2}};
int head,tail,flag;
void bfs(int a,int b)//a,b指终点坐标
{
int map[405][405]={0};
head=1;tail=1;flag=0;k=0;
que[tail].p=x;que[tail].q=y;
que[tail].s=0;
tail++;
map[x][y]=1;
while(head<tail)
{
for(int i=0;i<8;i++)
{
int tx=que[head].p+next[i][0];
int ty=que[head].q+next[i][1];
if(map[tx][ty]==0&&tx>=1&&tx<=n&&ty>=1&&ty<=m)
{
map[tx][ty]=1;
que[tail].p=tx;
que[tail].q=ty;
que[tail].s=que[head].s+1;
tail++;
}
if(tx==a&&ty==b)
{
flag=1;
break;
}
}
if(flag==1)
break;
head++;
}
if(flag==0)
k=-1;
else
k=que[tail-1].s;
}
int main()
{
scanf("%d %d %d %d",&n,&m,&x,&y);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(i==x&&j==y)
printf("0 ");
else
{
bfs(i,j);
printf("%d ",k);
}
}
printf("\n");
}
}
思考了很久,觉得可以用bfs一层一层搜索,用一个数组step[4005][4005]记录步数,可以避免多次搜索。因为可以一次搜索完,我重新写了一遍,不单列为一个函数。
#include<stdio.h>
struct note
{
int p;//横坐标
int q;//纵坐标
int s;//步数
}que[160010];
int main()
{
int map[405][405];//地图记录走过的路
int step[405][405];
int next[8][2]={{1,2},{-1,2},{1,-2},{-1,-2},{2,1},{-2,1},{2,-1},{-2,-1}};
int head,tail;
int n,m,x,y,i,j,k,tx,ty;
scanf("%d %d %d %d",&n,&m,&x,&y);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
step[i][j]=-1;//初始化所有步数为-1
step[x][y]=0;//起点初始化为0
head=1;tail=1;//队列初始化
map[x][y]=1;
que[tail].p=x;que[tail].q=y;que[tail].s=0;
tail++;
while(head<tail)
{
for(k=0;k<8;k++)
{
tx=que[head].p+next[k][0];
ty=que[head].q+next[k][1];
if(tx<1||tx>n||ty<1||ty>m)
continue;
if(map[tx][ty]==0)
{
map[tx][ty]=1;
que[tail].p=tx;
que[tail].q=ty;
que[tail].s=que[head].s+1;
step[tx][ty]=que[tail].s;//记录步数
tail++;
}
}
head++;
}
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
printf("%d ",step[i][j]);
printf("\n");
}
}
一直很纠结这个step[tx][ty]=que[tail].s应该放在哪里,考虑了一会,step[x][y]已经记录过了,所有放在37行后面很可以,还有tail++和head++不要搞反了,不然队列就会错乱。今天很摆,就写一题💤