第十三讲 BFS
DFS复习
DFS深度优先搜索,是将问题抽象成一种树结构,带着目的性的进行搜索,一路走到底,直到达到目标。深度优先搜索借助递归来实现,递归下去,回溯上来,如果没有走到底就发现下面的元素都无法满足题目要求时,则停止对该路的搜索。
DFS遍历步骤(图示)
- 从根节点1开始搜索
-
找出与此点邻接的且尚未遍历的点
-
遍历完所有节点后,将栈中元素弹出,DFS完成
例题
分析:因为是要求闭合*围成的曲线,第一步把四周的0,以及该0的四周的0变成*号,之后再枚举0的个数即可。
#include<iostream>
#include<cstring>
using namespace std;
char a[55][55];
int dx[]={0,-1,1,0},dy[]={1,0,0,-1};//搜索上下左右,可以少写几个if
int len=0;
void dfs(int x,int y){//参数为 为'0' 点的横纵坐标
a[x][y]='*';
for(int i=0;i<4;i++){
if(a[x+dx[i]][y+dy[i]]=='0')
dfs(x+dx[i],y+dy[i]);
}
return;
}
int main(){
cin>>a[0];
int m=1;
int len=strlen(a[0]);
while(cin>>a[m]) ++m;
for(int i=0;i<m;i++){//从边界开始搜索,将外围的染黑
if(a[i][0]=='0')
dfs(i,0);
if(a[i][len-1]=='0')
dfs(i,len-1);
}
for(int i=0;i<len;i++){
if(a[0][i]=='0')
dfs(0,i);
if(a[m-1][i]=='0')
dfs(m-1,i);
}
int sum=0;
for(int i=0;i<m;i++)
for(int j=0;j<len;j++)
if(a[i][j]=='0')
sum++;
cout<<sum;
}
BFS
BFS与DFS的区别与联系
BFS和DFS都属于优先搜索算法,BFS又称宽度优先搜索,它们的主要区别在于搜索方式不同,DFS是一个劲往下搜索并带有目的性的搜索,当不满足条件时就回溯,而BFS是一层一层的遍历。
数据结构 | 空间复杂度 | 可以解决的问题 | |
---|---|---|---|
DFS | 栈 | O(h) | 具体问题具体分析 |
BFS | 队列 | O(2^n) | 权值相同的最短路问题 |
如果按照BFS遍历该图中的元素,遍历顺序为 1->2->8->3->5->6->9->4->7
BFS模板
广度优先搜索算法的基本思想:
1、对于初始状态入队,设置初始状态为已访问
2、如果队列不为空时,出队队头元素,否则跳到第5步
3、检查出队的元素是否为最终解,如果是则跳到第5步。
4、对于出队的元素,检查所有相邻状态,如果有效并且未访问,则将
所有有效的相邻状态进行入队,并且设置这些状态为已访问,然后
跳到第2步重复执行
5、检查最后出队的元素是否为最终解,如果是输出结果,否则说明无解
void bfs(){
queue<int>q;
st[1]=1;//说明第一个点已经遍历过了
q.push(1);//把第一个变量入队
while(q.size()) {
int t=q.front();//取出队头元素
q.pop();
for(根据实际问题分析,遍历跟这个点相关的点){
if(满足题目的条件){
q.push(将该点入队);
}
}
}
为什么BFS能解决最短路问题?
如果要求图中 点1 和点4之间的最短路径,用BFS是十分好求的,为什么很容易,因为BFS是一层一层的遍历,它先遍历离顶点距离为1 的点,再遍历与顶点距离为2的点……
但是BFS只能解决权值相同的最短路问题,如果想要解决其他类型的最短路问题,需要用到其他的算法,以后在图论章节中会学到。
这道题求最短时间,并且移动一步所需要的时间是相同的
#include <iostream>
#include <cstdio>
#include <queue>
#include<cstring>
using namespace std;
int n,m,flag=0;
int dx[]={1,0,0,-1},dy[]={0,-1,1,0};
int vis[205][205];
string w[205];
bool valid(int x,int y){
return x>=0&&x<n&&y>=0&&y<m;
}
struct man
{
int x,y,step;
};
queue<man>q;
void bfs(man s)
{
q.push(s);//将营救队员初始位置入队
while(!q.empty())//队列非空
{
man t=q.front();
//printf("%d %d %d\n",t.x,t.y,t.step);
if(w[t.x][t.y]=='a'){printf("%d\n",t.step);flag=1;return;}//如果走到'a'点,就结束
else if(w[t.x][t.y]=='x'){t.step++;q.pop();q.push(t);w[t.x][t.y]='.';continue;}//遇到怪兽就把时间+1,再重新入队
q.pop();
for(int i=0;i<4;i++)//上下左右搜索
{
int xx=t.x+dx[i];int yy=t.y+dy[i];
if(valid(xx,yy)&&vis[xx][yy]==0&&w[xx][yy]!='#')
{
man k;
vis[xx][yy]=1;
k.step=t.step+1;k.x=xx;k.y=yy;q.push(k);
}
}
}
}
int main()
{
memset(vis,0,sizeof(vis));
scanf("%d%d",&n,&m);
man s;
for(int i=0;i<n;i++)cin>>w[i];//输入地图
for(int i=0; i<n; i++)
for(int j=0; j<m; j++)
{
if(w[i][j]=='r')//初始化营救队员所站的位置
{
s.x=i;
s.y=j;
s.step=0;
}
}
bfs(s);
if(flag==0)printf("NO ANSWER\n");
return 0;
}
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
typedef pair<int,int> PII;
char a[1500][250];
int dx[]={0,0,-1,1},dy[]={-1,1,0,0},len,k;//用dx,dy数组记录上下左右搜索坐标的变化情况
bool valid(int x,int y){//判断新坐标是否越界
return x>=1&&x<k&&y>=1&&y<=len;
}
void bfs(int x,int y){
a[x][y]='1';//将为0的点变为1
PII t=make_pair(x,y);//看不懂这个语句的,自行搜索STL 中的pair模板
queue<PII> q;
q.push(t);// 将该点坐标放进队头
while(q.size()){
PII t=q.front();//队头出队
q.pop();
for(int i=0;i<4;i++){//向它的四周搜素
int x1=t.first+dx[i];
int y1=t.second+dy[i];
if(valid(x1,y1)&&a[x1][y1]=='0'){//将新的符合条件的点入队
a[x1][y1]='1';
PII t=make_pair(x1,y1);
q.push(t);
}
}
}
}
int main(){
k=1;
cin>>(a[k++]+1);
while(cin>>(a[k]+1)){
k++;
}
len=strlen(a[1]+1);
for(int i=1;i<k;i++){
if(a[i][1]=='0'){
bfs(i,1);
}
if(a[i][len]=='0'){
bfs(i,len);
}
}
for(int i=1;i<=len;i++){
if(a[1][i]=='0')
bfs(1,i);//如果边界点为0,就向它上下左右四个方向搜索
if(a[k-1][i]=='0')
bfs(k-1,i);
}
int sum=0;
for(int i=1;i<k;i++)
for(int j=1;j<=len;j++)
if(a[i][j]=='0')
sum++;
cout<<sum<<endl;
}