对于广度、深度优先搜索算法的个人理解

2019.4.22更新

前言:本文用我自己能懂的方法进行叙述,不保证正确,本文涉及到的搜索均指bfs和dfs


搜索的基本介绍

 

假设一个问题可以用搜索算法来解决,那么这个问题一定可以被描述成这样:
有一个“类”,给出它的各个状态的约束条件(成为正解的条件),求满足题目要求的一个状态。

这个类的状态集(满足约束条件)也一定可以依据某种顺序被枚举出来,通过这种顺序枚举这些状态的过程叫搜索。

 

如果将每一个独立状态看做   一个图中的   一个节点,现在将这些节点

按照搜索过程中被访问到的顺序(每个状态被枚举到的顺序)排成一列,

这一个“列表”一定可以由一颗树的某种遍历顺序来表示(假设每一个节点只会被访问一次)。

 


广度优先搜索

 

bfs经常被用于求转移状态问题中的最小代价解

从宏观上来看,解决状态转移问题时,

广度优先搜索给我的第一感觉是 "一层一层的" ,看着很舒服,但是看了运用bfs思想做的各种例题,发现

我想的那种多线程并进 的 运行模式 在我现有的知识下很难实现,这应该很多人都发现过,但是广度优先搜索依然被称为“一层一层遍历”,

这一定是有原因的。

 

可以看一下bfs的搜索树。

在转移状态问题中,

对于每个状态节点的“下一层”,即此状态的转移状态,

在这个bfs搜索树中可以定义成它的儿子节点,所以对于“同时”中的“时”,

我的理解是它在这一个搜索树中的深度,“同层”同样。

 

对于同一深度的节点,到达它们的代价是到达它们在bfs搜索树中的father节点的代价加一个单位代价。

 

综上,对于    bfs为什么总是可以在状态转移问题中

得到最小代价解    这个问题,可以这么解释:

最优解一定在这个bfs算法的搜索树上,也就是说“最优解”(最小代价)的定义可以

被描述成这样:

  “此节点的深度”

为什么呢?

由于广搜的特性,从源点开始的每一“层”节点都会在宏观上的一次搜索中同时被访问到,

那么如果目标节点不在本层,目标节点一定在更大的某层;

如果目标节点在当前层,那么在之前各层的访问中,一定没有访问此目标节点(因为每层的访问总是访问当前层的所有节点),意即此时

目标节点是第一次被访问,访问目标节点的最小代价也就被发现了

又因为bfs算法的搜索树就是按照层次建立的,所以最优解可以被定义成“目标节点在bfs搜索树中的深度”。

 

上一道例题,可以让我们更好地理解bfs。

 

3、最少转弯问题(TURN)
【问题描述】
给出一张地图,这张地图被分为n×m(n,m<=100)个方块,任何一个方块不是平地就是高山。平地可以通过,高山则不能。现在你处在地图的(x1,y1)这块平地,问:你至少需要拐几个弯才能到达目的地(x2,y2)?你只能沿着水平和垂直方向的平地上行进,拐弯次数就等于行进方向的改变(从水平到垂直或从垂直到水平)的次数。例如:如图,最少的拐弯次数为5。

【输入格式】
第1行:n m
第2至n+1行:整个地图地形描述(0:空地;1:高山),
如(图)第2行地形描述为:1 0 0 0 0 1 0
第3行地形描述为:0 0 1 0 1 0 0
……
第n+2行:x1 y1 x2 y2 (分别为起点、终点坐标)

 

代码如下

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<queue>
 4 using namespace std;
 5 struct node{
 6     int x,y;
 7 };
 8 int sr,sc,er,ec,n,m,ans[120][120];//ans记录到某地点的最小转弯次数(因为我懒),sr为“start r”,er为“end r”,c类比。
 9 bool vis[120][120];//标记不能走的地方
10 int dr[4] = {1,-1,0,0};
11 int dc[4] = {0,0,1,-1};
12 queue<node>q;
13 int main() {
14     cin>>n>>m;
15     for(int i = 1; i <= n; ++i)
16         for(int j = 1; j <= m; ++j)    cin>>vis[i][j];//障碍物不能走
17     cin>>sr>>sc>>er>>ec;
18     ans[sr][sc] = -1;
19     vis[sr][sc] = 1;
20     q.push((node){sr,sc});
21     
22     while(!q.empty()) {
23         int r = q.front().x;
24         int c = q.front().y;
25         q.pop();
26         for(int i = 0; i < 4; ++i) {
27             int r1 = r, c1 = c;
28             while(r1>=1 && r1<=n && c1>=1 && c1<=m && !vis[r1+dr[i]][c1+dc[i]]) {//将此状态节点在搜索树中所有儿子节点都找出来
29                 r1 += dr[i], c1 += dc[i];
30                 vis[r1][c1] = 1;//走过的点不能走,因为 走 走过的点相当于浪费转弯次数
31                 ans[r1][c1] = ans[r][c] + 1;//体现了搜索树深度的度量单位
32                 if(r1 == er && c1 == ec) {
33                     cout<<ans[r1][c1];
34                     return 0;
35                 }
36                 q.push((node){r1,c1});
37             }
38         }
39     }//没有处理不能到达的情况,因为我懒
40     return 0;
41 }

可以看到,本题只是将bfs搜索树的深度定义为转弯次数而不是行走步数而已。

 

 


鸽子真可爱>w<

 

转载于:https://www.cnblogs.com/tztqwq/p/10747251.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值