专题:搜索-DFS与BFS

一.BFS:

也就是广度优先遍历,具体是从一个点向四周遍历马,一般是上下左右或者周围八个方向。bfs问题通常用来解决扩散类问题,也就是某个岛屿可以覆盖或者感染周围的岛屿类的问题,也常用于求连在一起的元素个数。通常呢,我们用队列来实现bfs问题。把某个点加入到队列中去,再取出队头,判断它四周有没有符合要求的元素,把符合的元素也加入到队列中去。

例题:acwing-奶牛选美:

听说最近两斑点的奶牛最受欢迎,约翰立即购进了一批两斑点牛。

不幸的是,时尚潮流往往变化很快,当前最受欢迎的牛变成了一斑点牛。

约翰希望通过给每头奶牛涂色,使得它们身上的两个斑点能够合为一个斑点,让它们能够更加时尚。

牛皮可用一个 N×M的字符矩阵来表示,如下所示:

................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....

其中,X 表示斑点部分。

如果两个 X在垂直或水平方向上相邻(对角相邻不算在内),则它们属于同一个斑点,由此看出上图中恰好有两个斑点。

约翰牛群里所有的牛都有两个斑点

约翰希望通过使用油漆给奶牛尽可能少的区域内涂色,将两个斑点合为一个。

在上面的例子中,他只需要给三个 .. 区域内涂色即可(新涂色区域用 ∗∗ 表示):

................
..XXXX....XXX...
...XXXX*...XX...
.XXXX..**..XXX..
........XXXXX...
.........XXX....

请帮助约翰确定,为了使两个斑点合为一个,他需要涂色区域的最少数量。

很基础的一道bfs题,就是bfs遍历两块斑点,再暴力找到两块斑点中的最短距离即可。

//acwing-奶牛选美
//暴力枚举+bfs
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
char a[53][53];
bool cnt[53][53];
vector<pair<int,int>>q1;
vector<pair<int,int>>q2;
queue<pair<int,int>>q;
int dx[4]={1,0,0,-1};
int dy[4]={0,1,-1,0};
int cn=0;
int ans=50000000;
void bfs(int n,int m){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
		if(cnt[i][j]==0&&a[i][j]=='X'){
			q.push({i,j});
			cnt[i][j]=1;
			while(!q.empty()){
				auto t=q.front();
				if(cn==0){
					q1.push_back(t);
				}
				else if(cn==1){
					q2.push_back(t);
				}
				q.pop();
				for(int i=0;i<4;i++){
					if(!cnt[t.first+dx[i]][t.second+dy[i]]&&a[t.first+dx[i]][t.second+dy[i]]=='X'){
						q.push({t.first+dx[i],t.second+dy[i]});
						cnt[t.first+dx[i]][t.second+dy[i]]=1;
					}
				}
			}
			cn++;
		}
		}
	}
}
int myans(int x1,int y1,int x2,int y2){
	return min(ans,abs(x1-x2)+abs(y1-y2));
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
		}
	}
	bfs(n,m);
    for(int i=0;i<q1.size();i++){
		for(int j=0;j<q2.size();j++){
			ans=myans(q1[i].first,q1[i].second,q2[j].first,q2[j].second);
		}
	}
	cout<<ans-1;
	return 0;
}

二.DFS

又叫做深度优先遍历,通常用来解决路径类问题,比如符合某些条件就可以一直往后遍历,直到不满足条件为止。一般用递归来实现,所以虽然dfs代码会更简洁一些,但是往往会难理解一点。当然熟练掌握后其实选DFS做题的会更多一点。

例题:acwing-大臣的旅费

很久以前,T王国空前繁荣。

为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

为节省经费,T 国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。

同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。

J是 T 国重要大臣,他巡查于各大城市之间,体察民情。

所以,从一个城市马不停蹄地到另一个城市成了 J 最常做的事情。

他有一个钱袋,用于存放往来城市间的路费。

聪明的 J 发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关。

具体来说,一段连续的旅途里,第 11 千米的花费为 1111,第 22 千米的花费为 1212,第 33 千米的花费为 1313,…,第 x 千米的花费为 x+10+10。

也就是说,如果一段旅途的总长度为 11 千米,则刚好需要花费 1111,如果一段旅途的总长度为 22 千米,则第 11 千米花费 1111,第 22 千米花费 1212,一共需要花费 11+12=2311+12=23。

J 大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?

其实题目就是找到最短的一条路径,这里有个规律,随便找一个点,然后找到离他最远的那个点u,再从u开始找到离u最远的点就是长的路径了,那么这里我们用dfs来找到最远点,双向dfs即可解决问题。

//双dfs求最长路径问题
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
//关键步骤
//1.选择一个结点开始遍历,找到最远的结点u
//2.再从u开始遍历第二次,找到最远结点走过的长度就是答案
int n;
struct node{
	int id,length;//记录该点能到达的位置和下个点的距离
};
vector<node>h[100003];
int dist[100003];
void dfs(int u,int father,int distance){
	dist[u]=distance;//记录当前结点走过的长度
	for(auto i:h[u]){//遍历h[i]结点
		if(i.id!=father){//下一个能走的结点不是父节点(也就是不能往回走)
			dfs(i.id,u,distance+i.length);//dfs下一个结点
		}
	}
}
int main(){
	cin>>n;
	for(int i=0;i<n-1;i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		h[a].push_back({b,c});//a能到达的下一个位置和长度
		h[b].push_back({a,c});//b能到达的下一个位置和长度
	}
	dfs(1,-1,0);//从第一个点开始遍历,-1是第一个点的父亲结点,0是目前走过的距离
	int u=1;
	for(int i=1;i<=n;i++){//遍历n个结点,找到走过路径最长结点的下标
		if(dist[i]>dist[u]){
		   u=i;
		}
	}
	dfs(u,-1,0);//u是走过最远的结点,从u开始遍历第二次,找到最远结点走过的长度就是最终答案啦
	for(int i=1;i<=n;i++){
			if(dist[i]>dist[u]){
			   u=i;
			}
		}
	int s=dist[u];
	long long int ans=0;
	for(int i=1;i<=s;i++){//求出走s这么长的路需要的路费
		ans+=10+i;
	}
	cout<<ans;	
	return 0;
}

当然啦,其实dfs与bfs差别并不算大,而且也是属于比较简单的算法,不过都是很重要的,所以多刷刷题掌握就好。

  • 28
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是C语言代码实现: ```c #include <stdio.h> #include <stdlib.h> #define MAX_VERTEX_NUM 20 // 图的最大顶点数 // 边表结点 typedef struct ArcNode { int adjvex; // 该边所指向的顶点的位置 struct ArcNode *next; // 指向下一条边的指针 } ArcNode; // 顶点表结点 typedef struct VertexNode { char data; // 顶点信息 ArcNode *firstarc; // 指向第一条依附该顶点的边的指针 } VertexNode; // 图的邻接表 typedef struct { VertexNode vertexes[MAX_VERTEX_NUM]; // 顶点表 int vexnum, arcnum; // 图的当前顶点数和边数 int kind; // 图的种类(有向图或无向图) } ALGraph; // 创建图的邻接表 void CreateGraph(ALGraph *G) { int i, j, k; char ch; ArcNode *p; printf("要建立的是有向图(1)还是无向图(0),请选择(输入1或0):"); scanf("%d", &G->kind); printf("请输入图的顶点数:"); scanf("%d", &G->vexnum); printf("请输入图的边数:"); scanf("%d", &G->arcnum); printf("请输入图的各顶点信息:\n"); for (i = 0; i < G->vexnum; i++) { getchar(); // 吸收上一行的回车符 printf("第%d个顶点信息:", i+1); scanf("%c", &ch); G->vertexes[i].data = ch; G->vertexes[i].firstarc = NULL; } printf("请输入边的信息,格式为:序号1,序号2(序号依次为0、1、2……):\n"); for (k = 0; k < G->arcnum; k++) { printf("请输入第%d条边:", k); scanf("%d,%d", &i, &j); // 新建一个边表结点 p = (ArcNode*) malloc(sizeof(ArcNode)); p->adjvex = j; // 将该边表结点插入到i所在顶点的边表头部 p->next = G->vertexes[i].firstarc; G->vertexes[i].firstarc = p; // 如果是无向图,还需要将该边表结点插入到j所在顶点的边表头部 if (G->kind == 0) { p = (ArcNode*) malloc(sizeof(ArcNode)); p->adjvex = i; p->next = G->vertexes[j].firstarc; G->vertexes[j].firstarc = p; } } } // 输出图的邻接表 void PrintGraph(ALGraph *G) { int i; ArcNode *p; printf("图的邻接表表示如下:\n"); for (i = 0; i < G->vexnum; i++) { printf("%d\t[%c](", i, G->vertexes[i].data); p = G->vertexes[i].firstarc; while (p != NULL) { printf("%d", p->adjvex); p = p->next; if (p != NULL) { printf(" "); } } printf(")\n"); } } int main() { ALGraph G; CreateGraph(&G); PrintGraph(&G); return 0; } ``` 运行程序后,按照提示输入相应的信息即可创建并输出图的邻接表。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值