BFS 与 DFS
深度优先搜索
深度优先搜索,简称
如果符合,保存此选项,枚举下一个选项;反之更换一个选项,尝试此选项是否合法;但如果此时
发现没有可枚举的选项了,也就是所有的选项有不合法,进行「回溯」操作,更换上一个选项。
这样,枚举时就可以避免枚举许多无效的状态,以此节省时间。
可以举一个这样的例子:
没有的话就放弃这个事情;
而枚举就是一个老实的人,他会老老实实的把所有活都干完,但却不知道自己干了一堆
没有意义的事。
刚刚所说
只需知道
void dfs(当前枚举到的选项){
if(所有的状态都枚举完毕){
输出/保存结果
return;
}
for(枚举当前选项)
if(这个选项是合法的){
记录这个选项
dfs(下一层枚举的状态);
取消这个选项
}
}
给定一个
方格的迷宫,迷宫里有 处障碍,障碍处不可通过。 在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。
给定起点坐标
和终点坐标 ,每个障碍物的 ,每个方格最多经过一次,问有多少种从起点坐标到终点坐标的方案。
这题我们可以枚举移动的方向,记录,再判断路上有没有障碍物,很明显时间允许,可以
这时,我们就可以使用
我们枚举上下左右四个方向,同时判断移动的方向有没有障碍物和有没有走过,分别用
再用一个
#include<bits/stdc++.h>
using namespace std;
int n,m,t,fx,fy,sx,sy,sum;
int a[6][6],note[6][6],next1[5][2] = {{114514,1919810},{0,1},{1,0},{0,-1},{-1,0}}; // 存储地图和走过的位置
void dfs(int x,int y){
if(x == fx && y == fy){ // 如果到了终点
sum ++; // 记录解的数量
return;
}
for(int i = 1;i <= 4;i ++){ // 枚举方向
int next_x = x + next1[i][0],next_y = y + next1[i][1]; // 计算下一步的坐标
if(!a[next_x][next_y] && !note[next_x][next_y]){ // 如果没有障碍物且也没走过
note[next_x][next_y] = 1; // 标记为走过
dfs(next_x,next_y); // 继续走迷宫
note[next_x][next_y] = 0; // 走完后恢复,方便下一次枚举
}
}
}
int main(){
cin>>n>>m>>t;
cin>>sx>>sy>>fx>>fy;
for(int i = 1;i <= t;i ++){
int x,y;
cin>>x>>y;
a[x][y] = 1;
} // 以上为输入
a[sx][sy] = 1; // 标记起点为障碍物,防止无限来回
for(int i = 0;i <= n + 1;i ++)
for(int j = 0;j <= m + 1;j ++)
if(i == 0 || i == n + 1 || j == 0 || j == m + 1)a[i][j] = 1; // 标记边缘为障碍物
dfs(sx,sy); // go,go,go!开始走迷宫
cout<<sum; // 输出解的数量
return 0; // 完结撒花
}
完美
可是,如果我们要的不是迷宫的一个解,而是最优解呢?
判断无解情况及时放弃罢了,有很大的随机性。
这时,
欲知后事如何,且看下回分解。
广度优先搜索
广度优先搜索,简称
而「分身」们又会分出新的分身;如果分出的分身走进了死胡同,没有可枚举的选项了,那么就不再进行「分身」。
最终,到达终点的「分身」一定是最优解,因为不是最优解的「分身」都被最优解分身抢先一步,路被最优解分身堵死了(被标记为走过)。
虽然因为要创造大量「分身」,不仅空间和时间都比
那它就不再有用,可以直接丢弃。根据这个特性,我们就可以使用「队列」来存储,依次出队各个「分身」,再将它们的子「分身」入队,
直到有「分身」到达终点或所有「分身」都进入死胡同,返回结果。
int bfs(初始参数){
queue<初始参数类型> q;
q.push(初始参数);
while(q.size()){
类型 f = q.front(); (获取队首)
q.pop(); (分身出队)
for(枚举选项){
if(选项合法){
if(到达目标状态)return 结果;
标记选项(防止重复枚举)
q.push(当前选项参数(分身);
}
}
}
return 无解;
}
一个迷宫由
行 列格子组成,有的格子里有障碍物,用 #
表示,不能移动至此处;有的格子是空地,用.
,可以移动至此处。在迷宫中移动有上下左右四种方式,每次只能移动一格。数据保证起点与终点为
.
。给定一个迷宫,求从左上角走到右下角最少需要走多少次?
枚举与记录部分思路与
并在最优解「分身」到达终点时返回最优解「分身」的步数,代码。
#include<bits/stdc++.h>
using namespace std;
char maps[100][100]; // 地图
int note[100][100],n,m; // 存储走过的位置
int next_x[] = {-1,1,0,0};
int next_y[] = {0,0,-1,1}; // 不写 if 的技巧
struct dot{
int x,y,step; // 定义一个结构体用于记录分身的位置与步数
};
int bfs(){
queue<dot> q; // 队列 q 用于存储分身
dot first;
first.x = 1,first.y = 1,first.step = 1; // 初始状态
q.push(first); // 初始状态入队
while(q.size()){ // 循环直到没有分身在队列中
dot f = q.front(); // 获取队首分身
q.pop(); // 将无用的队首分身杀掉(划)出队
for(int i = 0;i < 4;i ++){ // 枚举
dot next_dot = f;
next_dot.x += next_x[i];
next_dot.y += next_y[i];
next_dot.step ++; // 计算本分身的位置与步数
if(next_dot.x >= 1 && next_dot.x <= n && next_dot.y >= 1 && next_dot.y <= m && !note[next_dot.x][next_dot.y] && maps[next_dot.x][next_dot.y] != '#'){ // 判断是否超出边界、此处是否有障碍与是否走过
if(next_dot.x == n && next_dot.y == m)
return next_dot.step; // 如果到了终点返回
note[next_dot.x][next_dot.y] = 1; // 标记为走过
q.push(next_dot); // 入队新分身
}
}
}
}
int main(){
cin>>n>>m;
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++)
cin>>maps[i][j]; // 输入地图
cout<<bfs(); // 计算最优解并输出
return 0; // 完结撒花
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】