编程奇境:C++之旅,从新手村到ACM/OI算法竞赛大门(中级武器:搜索 DFS&BFS)

上一期我们讲了递归与递推,之前都是最最基础的武器,但这些武器不足以应对那些稍微厉害一点的怪兽,所以我们要掌握中级的武器,这一次的武器是:搜索算法。

DFS(深度优先搜索):勇往直前,不回头

想象你带着一根很长很长的绳子,每进入一个新的房间,就放一段绳子标记你的路径。你选择一条路一直走到底,直到遇到死胡同,才原路折返,捡起绳子,回到上一个岔路口,再尝试另一条路。就这样,你深入探索每一个可能的路径,直到找到宝藏。这个过程就像“走迷宫时一条路走到底,撞了南墙才回头”。

BFS(广度优先搜索):地毯式搜索,逐步推进

这次你不再用绳子,而是带了一队人马,大家排成一排一起前进。你们始终保持着队伍的宽度,遇到分叉路口,就一部分人继续沿当前路前进,其他人探索新路。这样,你们像波浪一样,一层一层地推进,直到触碰到宝藏所在的房间。这种方法保证了你总是先检查离起点近的房间,再检查远的房间,就像“湖面上扔一块石头,水波一圈一圈往外扩散”。

DFS

假设我们有一个简单的迷宫地图,用图形表示如下,其中S表示起点,E表示终点,.- 表示可以通过的路径,X表示墙壁。

 
S..X.
XX.X.
.X..X
..X.E

举个栗子

如果你使用DFS探索这个迷宫,你的行动可能是这样的:

  1. 从起点S出发,首先尝试向下走,因为那是第一个可选项。
  2. 到达下方的点后,发现是墙壁X,不能通过,于是回退到起点。
  3. 接下来尝试向右走,发现可以,于是继续向右移动到下一个点。
  4. 在新位置,向下是墙壁,向左是已探索过的点,因此向右走。
  5. 如此往复,找到了终点。

DFS的探索路径像是一条线,深入探索一条路径,遇阻则返回,再尝试其他路径。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
int n;
int endx,endy;
int a[1005][1005];//迷宫地图 
int v[1005][1005];//判断是否走过 
int dx[]={0,1,0,-1};//用来控制上下左右的 
int dy[]={1,0,-1,0};
void dfs(int x,int y)
{
	if(x==endx && y==endy)
	{
		cout<<"到终点啦"<<endl;
		return;
	}
	if(v[x][y]==1) return;//如果走过了 就不走
	v[x][y]=1;//标记走过
	for(int i=0;i<4;i++)
	{
		//下一步的位置 
		int xx=x+dx[i];
		int yy=y+dy[i];
		if(xx>=1&&xx<=n && yy>=1&&yy<=n && a[xx][yy]==0 && v[xx][yy]==0)//能往下走的条件 在范围内 是路 且没走过 
		{
			dfs(xx,yy);//递归调用 
			v[xx][yy]=0;//回溯的过程,因为如果这一步走下去发现最后到不了终点,就要走回来 
		}
	 } 
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cin>>a[i][j];//输入地图 
		}
	}
	dfs(1,1);//假设(1,1)是起点 
	return 0;
}

练习题: 

 P1605 迷宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

P1451 求细胞数量 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

P1162 填涂颜色 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

P2404 自然数的拆分问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

P2036 [COCI2008-2009 #2] PERKET - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

BFS 

举个栗子

想象一下,你正在组织一场校园寻宝活动,校园的地图可以抽象成一个网格,其中某些格子是道路(可以行走),而其他格子是建筑物(不能通过)。寻宝者从校门口出发,目标是尽快找到位于校园某个角落的宝藏。

场景设定

校园地图简化为一个二维网格,用字符表示如下:

S . . . X . . .
. . X . . . .
. X . . . X .
. . . X . . E

其中,S 表示起点(校门口),. 表示可以通行的道路,X 表示障碍物(如建筑),E 表示宝藏所在的位置。

BFS 解决方案

使用广度优先搜索策略,寻宝者将遵循以下步骤找到宝藏:

  1. 初始化:从起点 S 开始,将其放入一个叫做“队列”(Queue)的数据结构中,用来保存待探索的位置。同时,标记起点为已访问,以避免重复探索。

  2. 探索:从队列中取出第一个位置(即起点),检查它周围的每一个相邻格子(上、下、左、右,但不包括障碍物 X)。

  3. 扩展:如果相邻格子尚未访问过,就将它标记为已访问,并将其加入队列。这样,队列中始终保持着一层一层的待探索位置,从起点开始向外扩展。

  4. 终止条件:一旦队列中的某个位置是宝藏所在的位置 E,搜索结束。这时,我们就找到了从起点到宝藏的最短路径。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
int n;
char a[1005][1005];
int v[1005][1005];
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
void bfs(int x,int y)
{
	queue<pair<int,int> > q;//定义队列 
	q.push({x,y});//起点先放入队列 
	v[x][y]=1;//标记为走过 
	while(!q.empty())//循环遍历 
	{
		int ux=q.top().first;
		int uy=q.top().second;
		for(int i=0;i<4;i++)//四周都走 
		{
			int xx=ux+dx[i];
			int yy=uy+dy[i];
			if(xx>=1&&xx<=n && yy>=1&&yy<=n && a[xx][yy]=='.' && v[xx][yy]==0)//满足条件 
			{
				if(xx==endx && yy==endy)
				{
					cout<<"找到终点啦"<<endl;
					break;
				}
				q.push({xx,yy});//放入队列 
				v[xx][yy]=1; //标记为走过 
			}
		}
	}
}
int main()
{
	
	return 0;
}

 练习题:

P1443 马的遍历 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

P1135 奇怪的电梯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

P2895 [USACO08FEB] Meteor Shower S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

P1747 好奇怪的游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

总结

  • DFS是“一条道走到黑”,先深入探索一条路径,找不到再回头,适合找单一路径或确定是否存在某种解。
  • BFS是“地毯式搜索”,一层一层往外扩展,适合找最短路径,因为它总是先探索距离起点更近的节点。

在编程中,DFS通常用递归来实现,就像你不断深入房间再返回;而BFS通常用队列来辅助,保证按顺序探索各个层次的节点。

百看不如一练,只有实践才是进步最快的方式,更要独立思考,如果想不出来了就看题解,会有眼前一亮的感觉。好啦,今天就到这里吧。下一期再见,记得给专栏点个关注,明天接着来哦~

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值