其他分享
首页 > 其他分享> > DFS and BFS

DFS and BFS

作者:互联网

DFS and BFS

一. DFS的基本概念

深度优先搜索(Depth First Search,简称DFS):一种用于遍历或搜索树或图的算法。 沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当搜索遇到阻碍,如节点 的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点 的那条边的起始节点,换条路接着搜。
注意可以将dfs理解为栈,将各个子节点当作一个个的栈,然后将每个栈都遍历出来

问题示例

1.树或图的遍历问题

遍历无根树

#include<iostream>
#include<vector>

using namespace std;
const int N = 5e5+10;
int n,s;
vector<int> tre[N];//利用vector邻接链表来存树,表示与N相连接的点
void dfs(int u,int p)//u为子节点,p为父节点
{
	cout << u << endl;
	for(int i = 0;i < tre[u].size();i ++)//dev c++中不能使用for(int v;tre[u])来遍历vector容器(c++11特性)
	{
		if(tre[u][i]!=p) dfs(tre[u][i],u);
	}
} 
int main()
{
	cin >> n >> s;
	for(int i = 1,u,v;i < n;i ++)
	{
		cin >> u >> v;
		tre[u].push_back(v);
		tre[v].push_back(u);
	}
	dfs(s,0);//s为起点
	return 0;
}

遍历图

#include<iostream>
#include<vector>
using namespace std;
const int N = 5e6+10;
int n,m,s;
int vis[N];
vector<int> gra[N];
void dfs(int s)
{
	cout << s << endl;
	vis[s] = 1;//标记已经搜过的节点 
	for(int i = 0;i < gra[u].size();i ++)//在c++11中可以使用for(int v,tra[u])来遍历vector容器 
	{
		if(!vis[gra[u][i]]) dfs(gra[u][i]);//只搜没被标记的节点 
	}
}
int main()
{
	cin >> n >> m >>s;
	for(int i = 1,v,u;i <= n;i ++)
	{
		cin >> v >> u;
		gra[u].push_back(v);
		gra[v].push_back(u);
	}
}

时间复杂度:O(n)

2.暴力搜索问题

题目链接:https://www.luogu.com.cn/problem/P1605
题目描述:给定一个 方格的迷宫,迷宫里有 处障碍,障碍处不可通过。给定起点坐标和终
点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫中移动有上下左右
四种方式,每次只能移动一个方格。数据保证起点上没有障碍。
数据范围:1<= n,m <=5
样例输入:

2 2 1
1 1 2 2
1 2

样例输出:

1

思路;思路:暴力硬搜。我们用DFS的搜索策略,一条道走到黑,从起始点搜索出所有到终点的路径。在搜索的过程中遇到边界、障碍物和走过的点就返回,否则就接着搜下去,如果一个点无法继续走下去了,就回到上一个节点,同时把本节点的标记请0,换条路接着搜。

#include<iostream>
using namespace std;
int vis[10][10];//判断是否为障碍物,走过的点 
int sx,sy,fx,fy,n,m,k;
int dx[] = {-1,1,0,0};//方向数组  对应上下左右 
int dy[] = {0,0,-1,1};//方向数组  对应上下左右 
int cnt;
void dfs(int x,int y)
{
	if(x<1||y<1||x>n||y>m||vis[x][y]) return;//障碍物、被标记的点 ,边界都要返回 
	if(x==fx&&y==fy)
	{
		cnt++;
		return;
	}
	vis[x][y] = 1;
	for(int i = 0;i < 4;i ++) dfs(dx[i]+x,dy[i]+y);
	vis[x][y] = 0;//无路可走了,往上回溯,这点我不走了,标记为回0
}

int main()
{
	cin >> n >> m >> k;
	cin >> sx >> sy >> fx >> fy;
	for(int i = 0;i < k;i ++) 
	{
		int x,y;
		cin >> x >> y;
		vis[x][y] = 1;
	}
	dfs(sx,sy);
	cout << cnt << " ";
	return 0;
 } 

3.联通块问题

题目链接:http://poj.org/problem?id=1562
题目大意:给你一个 的格子,格子中只包含 * 和 @ ,相邻的 @ 被视为同一块(周围八个方向的
格子都视为相邻,问你格子中一共有多少块 @。
数据范围:多组输入,1<= n,m <=100

1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5
****@
*@@*@
*@**@
@@@*@
@@**@
0 0

样例输出:

0
1
2
2

思路:我们从每个未被访问过的 @ 出发,搜索它的八个方向上相邻的点,把 * 当成墙,遇到 * 就返回不搜了,遇到 @ 如果没被搜过就当成始点接着搜,搜完@后要标记,这样调用dfs的次数就是连通块的个数。因为每调用一次dfs就消除了一个连通块。
时间复杂度:O(n*m)

#include<iostream>
using namespace std;
char s[110][110];
int n,m;
int dx[]= {-1,-1,-1,0,0,1,1,1};//两个方向数组反别对应左上 上 右上 左 右 左下 下 右下
int dy[]= {-1,0,1,-1,1,-1,0,1};
int vis[110][110];//查询@是否被搜过
void dfs(int x,int y)
{
	if(x<1||y<1||x>n||y>m||vis[x][y]||s[x][y]=='*') return ;//如果@已经被搜过了就返回
	vis[x][y] = 1;
	for(int i = 0;i < 8;i ++) dfs(dx[i]+x,dy[i]+y);
}
int main()
{
	while(cin >> n >> m && n && m)
	{
		for(int i = 1;i <= n; i ++)
		{
			for(int j = 1;j <= m;j ++) 
			{
				cin >> s[i][j];
				vis[i][j] = 0;//多组输入要初始化
			}
		}
		int ans = 0;
        for(int i = 1;i <= n;i ++)
        {
        	for(int j = 1;j <= m;j ++)
        	{
        		if(!vis[i][j]&&s[i][j]=='@')//@必须为没被遍历过
        		{
        			dfs(i,j);
        			ans++;
				}
			}
		}
		cout << ans << endl;
	}
	return 0;
}

其中

for(int i = 1;i <= n; i ++)
		{
			for(int j = 1;j <= m;j ++) 
			{
				cin >> s[i][j];
				vis[i][j] = 0;//多组输入要初始化
			}

也可以写成

for(int i = 1;i <= n;i ++) cin >> s[i]+1;//s[i]是个二维字符数组,也可以当成一维字符串来处理 其中+1是为了保证下标从1开始,s[i]相当于指针
		for(int i = 1;i <= n; i ++)
		{
			for(int j = 1;j <= m;j ++) 
			{
				vis[i][j] = 0;
			}
		}

如果使用字符串的话下标只能从0开始,使用字符数组可以使下标从1开始,而且指针要加1即s[i]=1。

二.BFS基本概念

广度优先搜索(Breadth First Search,简称 BFS)从一个点出发,扩散到周围的点,按照层次优先的原则搜索完所有的点。形象点描述就像将一块石头扔到池塘里,石头所激发出的波纹传播到整个池塘的过程。
注意可以将其看作队列,遵守先进先出的原则
代码实现:

#include<iostream>
#include<queue>
using namespace std;
const int N = 5e6+10; 
int n,m,s;
int vis[N];
vector<int> gra[N];
void bfs(int s)
{
	queue<int> que;
	que.push(s);
	vis[s] = 1;
	while(!que.empty())
	{
		int u = que.front();
		que.pop();
		cout << u << endl;
		for(int i = 0;i < gra[s].size();i ++)
		{
			if(!vis[gra[s][i]]) continue;//入过队就不再搜了 
			que.push(gra[s][i]);
			vis[gra[s][i]] = 1;//入队后打上标记 
		}
	}
}
int main()
{
	cin >> n >> m >> s;
	for(int i = 0,u,v;i < n;i ++)
	{
		cin >> u >> v;
		gra[u].push_back(v);
		gra[v].push_back(u);
		
	}
	return 0;
}

问题示例

最短路

BFS的主要用途就是在边权为1的图上求最短路。当边权不为1时要用到floyd
例题:逃离迷宫
题目链接:https://ac.nowcoder.com/acm/contest/96/G?&headNav=www
题目大意:给你一个 的格子,格子中 ‘.’ 可以走 ,’#’ 不可以走,‘P’ 代表人物位置,‘K’ 代表钥匙(钥匙可以有多把),‘E’ 代表出口。每次可以花一单位时间向周围四个方向(上,下,左,右)走一格。逃出迷宫需要拿到钥匙到达出口,没有钥匙就无法通过出口,问你最少多久可以逃出迷宫,如果无法逃出则输出 “No solution”。
数据范围:多组数据 T 1 <= T <= 50 1 <= n,m <= 500
样例输入:

3
5 5
....P
##..E
K#...
##...
.....
5 5
P....
.....
..E..
.....
....K
5 5
P#..E
.#.#.
.#.#.
.#.#.
...#K

样例输出:

No solution
12
No solution

思路:由于bfs是广度搜索,本质是逐层便历,当某节点被第一次搜到时,这个距离便是它与起点的最短距离,对于此题我们首先要拿到钥匙,然后再从钥匙处走到出口,因此我们可以求出两段距离,一段是起点与钥匙的距离,另一段是钥匙与出口的距离(钥匙可以有多把),利用bfs可求出起点与所有点的最短距离,由于bfs是单源最短路,只能求一个点到多个点的最短距离,所以当求第二段距离时,可以求出口到各个钥匙的最短距离(逆向思维),可能会有多条道路,遍历出每把钥匙的位置,然后找出所有可能的道路,最后找出距离最小的道路,如果没有解,则输出No solution。

#include<iostream>
#include<queue>
using namespace std;
const int N = 150; 
const int INF = 0x3f3f3f3f;
struct Node{
	int x,y,w;//w为起点到该点的距离
};
char s[N][N];
int dis[2][N][N];//0与1分别表示两段不同的距离,0表示起点到钥匙,1表示钥匙到出口
int dx[] = {-1,1,0,0};
int dy[] = {0,0,-1,1};
int t,n,m;
queue<Node> que;
void bfs(int k,int x,int y)
{
	for(int i = 1;i <= n;i ++)
	{
		for(int j = 1;j <= m;j ++) dis[k][i][j] = INF;//表示不能到达该点
	}
	que.push({x,y,0});
	while(!que.empty())
	{
		Node t = que.front();
		que.pop();
		for(int i = 0;i < 4;i ++)
		{
			int x = t.x+dx[i],y = t.y+dy[i];
			if(x<1||y<1||x>n||y>m||s[x][y]=='#'||s[x][y]=='E'||dis[k][x][y]!=INF) continue;//s[x][y]=='E',这个条件容易被忽略,在求第1段距离时不能穿过'E'
			que.push({x,y,t.w+1});
			dis[k][x][y] = t.w+1; 
		}
	}
	
}
int main()
{
	cin >> t;
	while(t--)
	{
		cin >> n >> m;
		for(int i = 1;i <= n;i ++) cin >> s[i]+1;
		for(int i = 1;i <= n;i ++)
		{
			for(int j = 1;j <= m;j ++)
			{
				if(s[i][j]=='P') bfs(0,i,j);
				if(s[i][j]=='E') bfs(1,i,j); 
			}
		}
		int ans = INF+INF;
		for(int i = 1;i <= n;i ++)//枚举所有的钥匙点
		{
			for(int j = 1;j <= m;j ++)
			{
				if(s[i][j]=='K') ans = min(ans,dis[0][i][j]+dis[1][i][j]);//可能会有多个解,找出最小的
			} 
		}
		if(ans>=INF) cout << "No solution\n";
		else cout << ans << endl;
	}
}

标签:int,cin,dfs,BFS,vis,que,DFS,gra
来源: https://blog.csdn.net/yyds_hpu/article/details/123612050