这是《C++算法宝典》算法篇的第08节文章啦~
如果你之前没有太多C++基础,请点击👉专栏:C++语法入门,如果你C++语法基础已经炉火纯青,则可以进阶算法👉专栏:算法知识和数据结构👉专栏:数据结构啦
目录
📕广搜走迷宫找最短路径
📕如何找到最短路径?
🧠问题建模
如何表示迷宫地图等信息呢?
如何表示每次移动的过程?
如何实现搜索的过程呢?
问题参考程序
🧠广度优先搜索模板
📕训练:森林救援
参考代码
广搜走迷宫找最短路径
现在有一个4*4的迷宫,小知在迷宫的左上角,迷宫出口在右下角,小知体力有限,他希望尽快走出迷宫,请你告诉小知最少需要走多少步(每个格子不能重复走动)。
迷宫中显示0的点,是不可以走的。小知每次只能到达相邻的上下左右4个格子。
如何找到最短路径?
我们可以按照这样的思路去找:
- 从起点出发,检查第1步可以到达的所有点,判断是否为终点。
- 依次从第1步到达的点出发, 检查判断第2步可以到达的点是否为终点。
- 依次从第2步到达的点出发,检查判断第3步可以到达的点是否为终点。
- 依次从第3步到达的点出发,检查判断第4步可以到达的点是否为终点。
- 依次从第4步到达的点出发,检查判断第5步可以到达的点是否为终点。
6.找到终点,程序结束,步数为5。
问题建模
如何表示迷宫地图等信息呢?
1.使用一个4*4的二维数组maze[][]来存储迷宫信息,如果值位0表示不可走,1表示可走。
2.使用一个4*4的二维数组used[][]来标记是否走过,没走过为0,走过的话为1。
例:used[1][2]=1,表示maze[1][2]已经走过。
如何表示每次移动的过程?
每次移动,实际上就是坐标的变化。
上:行标-1,列标不变。
下:行标+1,列标不变。
左:行标不变,列标-1。
右:行标不变,列标+1。
我们可以用一个二维数组表示移动的方向。
例:当前坐标为(1,2),向上移动就是:
(1+fx[0][0],2+fx[0][1])得到(0,2)。
如何实现搜索的过程呢?
1.我们需要使用队列(que)来实现,用一个结构体表示每次找到的点的坐标信息以及步数(x,y,cnt)。
2.将起点入队。
3.取出队首元素,队首后移(head++),将队首元素上下左右的四个点依次入队(步数cnt要+1),同时判断是否到达终点,若到达终点则终止程序。
4.重复步骤3,直到找到终点或者队列为空(即head>tail)
问题参考程序
#include<bits/stdc++.h>
using namespace std;
struct wz{int x,y; //坐标int cnt; //步数
} que[1000],front,a; // front用来存每次取出的队首元素,a存起点信息
int maze[5][5],used[5][5];
int fx[4][2]={{0,-1},{0,1},{-1,0},{1,0}};
int head=1,tail=1,sx=1,sy=1,ex=3,ey=4,flag=0;//sx,sy,ex,ey分别表示开始,结束的坐标
void bfs(wz a);
int main()
{for(int i=1;i<=4;i++)for(int j=1;j<=4;j++)cin>>maze[i][j];a.x=sx,a.y=sy,a.cnt=0;bfs(a);cout<<que[tail].cnt;return 0;
}
void bfs(wz a){que[head]=a;//起点入队used[a.x][a.y]=1;while(head<=tail){ //当队列非空时搜索front=que[head]; //取出队首head++;//队首出队for(int i=0;i<4;i++){ //检查队首元素四个方向的点int nx=front.x+fx[i][0], ny=front.y+fx[i][1];//下一次前进点的坐标if(nx>=1&&nx<=4&&ny>=1&&ny<=4&&!used[nx][ny]&&maze[nx][ny]){//点要在地图内,且未被走过,且非障碍tail++; //队尾后移used[nx][ny]=1; //标记用过que[tail].x=nx; que[tail].y=ny;que[tail].cnt=front.cnt+1; //点入队,步数+1}if(nx==ex&&ny==ey){ //到达终点head=tail+1;//退出while循环break;//退出for循环}}}
}
广度优先搜索模板
上面的走迷宫的过程就是一个广度优先搜索的过程:从初始状态出发->1次转移(1步)能够到达的所有状态->2次转移(2步)能够到达的所有状态...n次转移能到达的所有状态。
我们常用广度优先搜索处理两种问题:
1.最短路径问题。
2.是否有路线的问题。
void bfs(State a){队头指针 head=1,队尾指针 tail=1;起始状态a入队while(head<=tail){取出队首元素 State=que[head];队头指针后移 head++;尝试从队首元素出发可以得到的n个状态for(int i=1;i<=n;i++){if(满足条件){队尾后移,tail++状态入队,并标记}if(到达终点) {退出for和while循环。}}}
}
训练:森林救援
正在森林中探险的小知,收到了一个求救信号。森林中有人受伤了,小知需要尽快赶到伤者那里帮忙。森林可以看做是一个m*n的地图,k表示小知,p表示伤者,森林中可以行走的地方用'*'表示,其他符号表示不可走。
小知只能上下左右移动,请你告诉小知,他最少需要走多远。
【输入描述】第一行输入m和n,分别表示地图的行和列
第二行输入地图的内容,其中k表示小知的位置,p表示伤者的位置,*表示可以行走的地方,其他符号均不可行走
【输出描述】如果小知能走到伤者的位置,输出其最少距离
如果小知走不到伤者的位置,输出No
【输入样例】
4 4
k * * *
* & * *
* * * p
* * * *
【输出样例】
5
参考代码
#include<bits/stdc++.h>
using namespace std;
struct wz{int x,y;int cnt;
} que[1000],front,a;
char maze[40][40];
int fx[4][2]={{0,-1},{0,1},{1,0},{-1,0}},used[40][40];
int head=1,tail=1,m,n,flag=0;
void bfs(wz a);
int main()
{cin>>m>>n;for(int i=1;i<=m;i++){for(int j=1;j<=n;j++){cin>>maze[i][j];if(maze[i][j]=='k'){a.x=i; a.y=j; a.cnt=0;}}}bfs(a);if(flag)cout<<que[tail].cnt;elsecout<<"No";return 0;
}
void bfs(wz a){que[head]=a;while(head<=tail){front=que[head];head++;for(int i=0;i<4;i++) {int nx=front.x+fx[i][0];int ny=front.y+fx[i][1];if(!used[nx][ny]&&(maze[nx][ny]=='*'||maze[nx][ny]=='p')){tail++;used[nx][ny]=1;que[tail].x=nx;que[tail].y=ny;que[tail].cnt=front.cnt+1;}if(maze[nx][ny]=='p'){flag=1;head=tail+1;break;}}}
}
从入门到算法,再到数据结构,查看全部文章请点击此处http://www.bigbigli.com/