算法笔记01——深度优先搜索(DFS)/宽度优先搜索(BFS)
深度优先搜索(DFS)
从某个状态开始,不断地转移状态直到无法转移,然后回退到前一步的状态,继续转移到其他状态,如此不断重复,直至找到最终的解。深度优先搜索从最开始的状态出发,遍历所有可以到达的状态。由此可以对所有的状态进行操作,或者列举出所有的状态。
第一道例题
分析: 从a1开始决定每个数加或者不加,在全部n个数都决定后在判断它们的和是不是k即可。
(在程序中使用的是0起始的下标原则,题目描述中则是1开始的,这一点要避免搞混。)
如何实现这个搜索呢,参见下面的代码。
#include<iostream>
using namespace std;
const int MAX=2000;
bool dfs(int i,int sum);
int n,k;
int a[MAX];
int main(){cin>>n;4for(int j=0;j<n;j++){cin>>a[j];}cin>>k;if(dfs(0,0)){cout<<"Yes"<<endl;}else{cout<<"No"<<endl;}return 0;
}
//已经从前i项得到了和sum,然后对于i项之后的进行分支
bool dfs(int i,int sum){//如果前n项都计算过了,则返回sum是否与k相等if(i==n) return sum==k;//不加上a[i]的情况if(dfs(i+1,sum)) return true;//加上a[i]的情况if(dfs(i+1,sum+a[i])) return true;//无论是否加上a[i]都不能凑成k就返回falsereturn false;
}
第二道例题
分析: 从任意的w开始,不停的把邻接的部分用’ . '代替。1次DFS后与初始的这个w连接的所有w都被替代换成了’ . ',因此直到图中不再存在w为止,总共进行DFS的次数就是及答案了。
如何实现这个搜索呢,参见下面的代码。
#include<iostream>
using namespace std;
const int MAX=100;
int N,M;
char field[MAX][MAX];//园子
void dfs(int x,int y);
int main(){int count=0;cin>>N>>M;for(int i=0;i<N;i++)for(int j=0;j<M;j++)cin>>field[i][j];for(int i=0;i<N;i++)for(int j=0;j<M;j++)if(field[i][j]=='w'){//从有w的地方开始dfsdfs(i,j);count++;}cout<<count<<endl;return 0;
}
//现在的位置(x,y)
void dfs(int x,int y){//将现在的位置替换为.field[x][y]='.';//循环遍历移动的8个方向for(int dx=-1;dx<=1;dx++){for(int dy=-1;dy<=1;dy++){//向x方向移动dx,向y方向移动dy,移动的结果为(nx,ny)int nx=x+dx,ny=y+dy;//判断(nx,ny)是不是在园子内,以及是否有积水if(nx>=0&&ny>=0&&nx<N&&ny<M&&field[nx][ny]=='w')dfs(nx,ny);}}
}
宽度优先搜索(BFS)
宽度优先搜索总是先搜索距离初始状态近的状态。也就是说,它是按照开始状态——只需1次转移就可以到达的所有状态——只需2次转移就可以达到的状态——…这样的顺序一层一层进行搜索。
0 | 1 | 2 | 3 | … |
---|---|---|---|---|
1 | 1 | 2 | 3 | … |
2 | 2 | 2 | 3 | … |
3 | 3 | 3 | 3 | … |
… | … | … | … | … |
宽度优先搜索利用了队列,搜索时首先将初始状态添加到队列里,此后从队列的最前端不断取出状态,把从该状态可以转移到状态中尚未访问过的队列,如此反复,直至队列被取空或者找到了问题的解。通过观察这个队列,我们就可以知道所有的状态都是按照距初始状态由近及远的顺序被遍历的。
一道例题
分析: 这个问题中,状态仅仅是目前所在位置的坐标,因此可以构造成pair或者编码成int来表示状态。只要将已经访问过的状态用标记管理起来,就可以很好的做到由远及近的搜索。本题要求最短距离,不妨用d[N][M]数组把最短距离保存起来。初始时用最大的常数INF来初始化它,这样尚未到达的位置就是INF,也就同时起到了标记作用。
如何实现这个搜索呢,参见下面的代码。
#include<iostream>
#include<queue>
using namespace std;
const int MAX=100;
const int INF=100000;
char maze[MAX][MAX]; //表示迷宫字符串的数组
int N,M;
int sx,sy; //起点坐标
int gx,gy; //终点坐标
int d[MAX][MAX]; //到各个位置的最短距离的数组
//4个方向移动的向量
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
//使用pair表示状态时,使用typedef会更加方便一些
typedef pair<int,int> P;
int bfs();
int main(){cin>>N>>M;for(int i=0;i<N;i++)for(int j=0;j<M;j++){cin>>maze[i][j];//找到起点位置if(maze[i][j]=='S'){sx=i;sy=j;}//找到终点位置if(maze[i][j]=='G'){gx=i;gy=j;}}int res=bfs();cout<<res<<endl;return 0;
}
//求从(sx,sy)到(gx,gy)的最短距离
//如果无法到达,则是INF
int bfs(){queue<P> que;//把所有位置都初始化为INFfor(int i=0;i<N;i++)for(int j=0;j<M;j++)d[i][j]=INF;//将起点加入队列,并把这一地点的距离设置为0que.push(P(sx,sy));d[sx][sy]=0;//不断循环直到队列的长度为0while(que.size()){//从队列最前面端取出元素P p=que.front();que.pop();//如果取出状态已经是终点,则结束搜索if(p.first==gx&&p.second==gy)break;//4个方向循环for(int i=0;i<4;i++){//移动之后的位置记为(nx,ny)int nx=p.first+dx[i],ny=p.second+dy[i];//判断是否可以移动以及是否已经访问过if(nx>=0&&ny>=0&&nx<N&&ny<M&&maze[nx][ny]!='#'&&d[nx][ny]==INF){//可以移动的话,则加入队列,并且到该位置的距离确定为到p的距离+1que.push(P(nx,ny));d[nx][ny]=d[p.first][p.second]+1;}}}return d[gx][gy];
}
宽度优先搜索按照距开始状态由近及远的顺序进行搜索,因此可以很容易地用来求最短路径,最少操作之类问题的答案。
总结
宽度优先搜索与深度优先搜索一样,都会生成所有能够遍历到的状态。但是递归函数可以很简短地编写,而且状态的管理也更简单,所以大多数情况下还是用深度优先搜索实现。反之,在求取最短路时深度优先搜索需要反复经过同样的状态,所以此时还是使用宽度优先搜索为好。
参考资料
《挑战程序设计竞赛(第二版)》
算法笔记01——深度优先搜索(DFS)/宽度优先搜索(BFS)相关推荐
- 深度优先搜索和宽度优先搜索
深度优先搜索和宽度优先搜索 bfs和dfs都是遍历图的方法.dfs是不撞南墙不回头,bfs慢慢来,一层一层来. 类型 空间(h为高度) 时间(h为高度) 采用的数据结构 特点 DFS O(h) O( ...
- 数据结构与算法—图论之dfs、bfs(深度优先搜索、宽度优先搜索)
文章目录 前言 邻接矩阵和邻接表 深度优先搜索(dfs) 宽度(广度)优先搜索(bfs) 总结与比较 前言 在有向图和无向图中,如果节点之间无权值或者权值相等,那么dfs和bfs时常出现在日常算法中. ...
- ACM算法笔记(十)深度优先搜索与宽度优先搜索
深度优先搜索 事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次. 下面 ...
- 深度优先搜索与宽度优先搜索
深度优先搜索 简称:DFS 基本思路 深度优先遍历图的方法是,从图中某顶点v出发: (1)访问顶点v: (2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历:直至图中和v有路径相通的顶点都被访问 ...
- 广度优先搜索、宽度优先搜索,《学点算法吧,Python》
一.广度优先搜索 广度优先搜索算法(又称宽度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型. 广度优先搜索是一种用于图的查找方法,可以帮助解决两个问题: 从节点A出发,有前 ...
- 算法学习(九)之“宽度优先搜索”
什么是宽度优先搜索? 面试题: 小岛问题 题目: 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量. 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相 ...
- Luck 的象棋(深度优先搜索、宽度优先搜索)象棋棋子“马”走日,求最短步数
题目描述 Luck 有一个象棋棋子"马",它在一个 n*m 的矩阵棋盘,棋盘左上角坐标为(1,1),右下角坐 标为(n,m),它现在的位置是(sx, sy),现在 Luck 想让它 ...
- 生成图-深度优先搜索/宽度优先搜索
问题提出: 考虑如下图所示的简单图所表示的缅因州的道路系统.在冬天里保持道路通路通畅的唯一方式就是经常扫雪.高速公路部分希望只扫尽可能少的道路上的雪,而确保总是存在连接任何两个乡镇的干净道路.如何才能 ...
- 野人与传教士——宽度优先搜索(完整报告,含全部代码)
题目: 野人与传教士渡河问题:3个野人与3个传教士打算乘一条船到对岸去,该船一次最多能运2个人,在任何时候野人人数超过传教士人数,野人就会把传教士吃掉,如何用这条船把所有人安全的送到对岸?在实现基本程 ...
最新文章
- 进程(process)和线程
- 什么是“根”?没有多少人思考,万物的“根”在哪里?
- ctf-cybrics
- [USACO1.3]号码锁 Combination Lock
- 库存管理系统软件测试,药房库存管理系统模块测试用例
- Kali 2.0 采用ssh连接登陆
- 每个 Linux 用户都应该知道的命令行技巧
- linux下的gpio转串口驱动,X-026-KERNEL-Linux gpio driver的移植之gpio range
- 分布式存储系统设计 - Gossip
- 为什么我的计算机无法评分,高手看看我的电脑鲁大师怎么评分这么低啊
- 测试员35岁以后找不到工作?问完了几千人后,我们得到了答案......
- android的按钮状态,保持android按钮选择状态
- linux下alias命令具体解释
- 美国在线教育的启示:教育领域正在革命
- 软件测试HW3 主路径覆盖测试
- python 自相关_自相关与偏自相关的简单介绍
- Tensorflow使用LSTM实现中文文本分类(1)
- 图标显示方框问题的一种原因
- 传说之下手机如何使用debug_传说之下手机版怎么设置按键 按键设置方法
- 哼唱识别(query by humming)
热门文章
- Python学习总结----基础篇3
- 三星Note3使用技巧之S搜索功能如何使用
- (转)一张图看懂H5测试
- 李小铭计算机专业应聘书作文,小学四年级应聘书作文
- amazon sqs_使用Amazon SQS进行基于云的消息传递
- 【Chrome浏览器插件开发】浏览器插件运行机制02之实战开发出一款Google浏览器插件——含源码全部过程 (建议在Linux环境下)
- 【DFS】洛谷P1706:全排列问题(C++)
- 未知的connectionStrings来自哪里?
- Shader Graph 7.5.1
- react 虚拟 dom