广搜讲解目录

  • 广搜回顾
  • 广搜题目
    • noj1541八数码问题
      • 题目描述:
      • 题目要求即样例
      • 题目分析
      • 题解代码【注解详细】---理解BFS
    • noj1652 僵尸来了
      • 题目描述
      • 题目要求及样例
      • 题目分析
      • 题目代码(注解详细)
    • noj1653 僵尸又来了
      • 题目描述
      • 题目要求及样例

广搜回顾

上一篇博客我已经和大家从整体上分析了广搜bfs,深搜dfs和递归,对于广搜,举了一道例题,就是noj的加一乘二平方,当时借助这个题目就和大家分享了bfs求解的一般模式----取出根节点u,扩展u,当然使用的是while循环

这个题目非常简单,没有难度,因为这里的分支都确定好了,就三种分支;并且在每一个结点处的状态也很简单,就简单的一个步数,用int记录就可以了

广搜题目

首先和大家分享一个非常有趣的问题,八数码问题,这不是一个数学问题,而是一个计算机问题,类似于我们小时候玩的推箱子,拼图游戏,一起来看一下

noj1541八数码问题

题目来源 : noj 1541

题目描述:

在九宫格里放在1到8共8个数字还有一个是空格,与空格相邻的数字可以移动到空格的位置,问给定的状态最少需要几步能到达目标状态(用0表示空格):
1 2 3
4 5 6
7 8 0

题目要求即样例

输入:

输入一个给定的状态。

输出:

输出到达目标状态的最小步数。不能到达时输出-1。

输入样例:

1 2 3
4 0 6
7 5 8

输出样例:

2

题目分析

看到题目,先看题目的输出:输出的是步数,---- 再看一下题目的描述,要求的是移动数字,每一个都有多种移动方式,—一下子就有思路了,步数加上题目的树结构描述----广搜首选了,这是根据我们之前的经验推出来的题目的解决办法

好了,也许会问,哪里来的树结构?0可以移动到于与之相邻的位置,这里样例中的0有两个位置可以移动,6和8的位置;到了6的位置,又有两个位置可以移动3和5的位置;到了5的位置,有4个位置可以移动······形成了一个树结构,那求步数就简单了嘛,一层一层搜索,找到目标状态,输出层号就可

完全离不开我们之前分析的bfs的一般结构,还是用队列来存储每一个结点的状态;这里的每个结点所对应的状态是一个九宫格?

这怎么弄?你不可能用一个二维数组来存储状态吧,那如何比较?难道用矩阵的方式?所以用二维数组是不现实的,这里使用的是将九宫格转化为一串数字存储,那八皇后问题,不也是降维了,所以这里降维是肯定的

所以就转为一串数字执行

那按照bfs一般思路,那就是用两个数组,一个记录步数,一个记录是否用过

int used[1000000000];//最大还是九位数
int step[1000000000];//记录步数
int mover[4] = { -1,0,+1,0 };//上左下右移动
int movec[4] = {0,-1,0,+1};

但是按照常识就知道这超出范围了,这可又怎么办,每一个状态都是一个九位数,所以这里就用到了之前java中分析的映射map

那这里要如何?

使用c++库中的map就可以了,map映射为一一映射,一个键只对应一个值,和java一样,键是set,所以只允许出现一次,否则添加的时候就会自动删除,这里使用C++库中的mag.count方法,为0才让它继续运行. 这里它就相当于之前的两个数组

map<int, int> useandstepmap;//一一映射关系,并且和set一样,只能出现一次
int mover[4] = { -1,0,+1,0 };//上左下右移动
int movec[4] = {0,-1,0,+1};

题解代码【注解详细】—理解BFS

#include<iostream>
#include<queue>
#include<map>using namespace std;queue<int> q; //还是一个队列,来记录位置
map<int, int> useandstepmap;//一一映射关系,并且和set一样,只能出现一次
int mover[4] = { -1,0,+1,0 };//上左下右移动
int movec[4] = {0,-1,0,+1};int inputdata()
{int t = 0;//将二维数组转化为一串10进制数int num;for (int i = 0; i < 9; i++){cin >> num;t = t * 10 + num;//这样最开始输入的数据就是最高位}return t;
}
void init(int root)
{useandstepmap[root] = 0;q.push(root); //步骤一模一样
}int moveto(int u, int dire)
{//先解码,再转码,不然不好操作int loadnum;loadnum = u;//要不断变化int statu[3][3];//最开始转码是从左上到右下,解码反过来int row, col;//记录初始空格位置int r, c; //记录需要移动的位置for (int i = 2; i >= 0; i--){for (int j = 2; j >= 0; j--){statu[i][j] = loadnum % 10;loadnum = loadnum / 10;if (statu[i][j] == 0){row = i;col = j;}}}r = row + mover[dire];c = col + movec[dire];//这里因为固定,就不需要讨论,在创建数组时就讨论好了//移动空格statu[row][col] = statu[r][c];statu[r][c] = 0;//转码int v = 0;for (int i = 0; i < 3; i++){for (int j = 0; j < 3; j++){v = v * 10 + statu[i][j];}}return v;
}bool canMove(int u, int dire)
{int loadnum = u;//转码判断位置是否合法int statu[3][3];int row, col;//记录空格位置for (int i = 2; i >= 0; i--){for (int j = 2; j >= 0; j--){statu[i][j]= loadnum % 10;loadnum = loadnum / 10;if (statu[i][j] == 0){row = i;col = j;}}}row = row + mover[dire];col = col + movec[dire];int v;v = moveto(u, dire);if (row < 3 && row >= 0 && col >= 0 && col < 3)//判断使用就在map中{return true;}return false;
}int bfs()
{//一般情况就是有4个子支可以移动while (!q.empty()){int u, v;//1.取出结点uu = q.front();q.pop();//扩展u{还是使用for循环来表示子支}for (int i = 0; i < 4; i++)//0 1 2 3 上左下右{if (canMove(u, i))//移动的状态{v = moveto(u, i);if (v == 123456780){return useandstepmap[u] + 1;}if (useandstepmap.count(v) == 0){useandstepmap[v] = useandstepmap[u] + 1;q.push(v);}}}}
}int main()
{//这个题目只是状态复杂了一些,其他就是一般的bfsint root,step;root = inputdata();//初始状态init(root); //初始化一样step = bfs();cout << step << endl;
}

我使用c++的原因是oj上不支持java,c++用起来也很方便,特别是这里的map成功解决一维数组越界问题

noj1652 僵尸来了

其实我准备出的是推箱子这个游戏的,但是其实推箱子也和上面的移动差不多,并且C站里也有相应题目了,所以这里分享1652这个题目,因为这是oj里才出不久的题目,还没有详解

题目描述

僵尸要来佳佳家做客了,佳佳把花园布置了一下,你拿到了花园的地图(以二维矩阵的形式表示)以及起点和佳佳家的位置。花园里某些位置有地刺,僵尸过的时候每次需要消耗一个单位的生命值,僵尸有一定数量的生命值,看看最聪明的僵尸能否在天亮前到达你的家里?能的话,最少花费多少时间。

题目要求及样例

输入:

输入的第一行包含三个整数:m,n,t。代表m行n列的地图和僵尸的生命值t。m,n都是小于200的正整数,t是小于10的正整数,第2行开始的m行是m行n列的花园地图,其中!代表起点,+表示佳佳家。*代表通路,w代表地刺。

输出:

输出僵尸到达佳佳家最少需要花费的时间。如果无法到达,则输出-1。

输入样例:

4 4 2
w!ww
**ww
www+
****

输出样例:

6

题目分析

又又又是一个移动问题,要求输出的是最少到达的时间,其实就是最少的到达步数,样例输出的结果为6,分析一下,僵尸这里有2点的生命值,那么路径就是先一直往下3步,右走2步,上走一步,就是6步,就是这样子,僵尸行走的方向还是有4个,上下左右,和上面的拼图一样,

明确使用bfs,接下来分析状态,上一个题目的状态就是二维数组的一个新的形态,步数为因变量,所以记录形态就使用一维数组,只是因为越界,所以,这里就把used一起纳入组成map,v为键,used为值,使用map.count实现了自动避开used,这里的状态复杂了一点,因为这里每个状态就是其目前所处坐标还有其生命值,按照dp的话,这里应该是一个三位dp

如果是dp就使用step【row】【col】【hp】来表示状态的步数

这里我们使用深搜,那就使用结构体表示结点就可以了

其他的基本思路,还是套用之前博客中的bfs的一般模式,这里只是需要注意细节,吃掉第一行的回车,输入时

题目代码(注解详细)

#include<iostream>
#include<queue>using namespace std;struct node {int row;int col;int hp;
};node start, target;//记录结点
int m, n, t;
char  garden[200][200];//二维数组【花园】
queue<node> q; //结点队列
int used[200][200][10];
int step[200][200][20];//和之前一样使用记录步数int mover[4] = { -1,0,+1,0 };//上左下右
int movec[4] = { 0,-1,0,+1 };void input()//忘记吃掉第一行的回车
{cin >> m >> n >> t;start.hp = t;cin.get();for (int i = 0; i < m; i++){for (int j = 0; j < n; j++){garden[i][j] = cin.get();if (garden[i][j] == '!'){start.row = i;start.col = j;}if (garden[i][j] == '+'){target.row = i;target.col = j;}}cin.get();}
}void init(node start)
{for (int i = 0; i < 200; i++){for (int j = 0; j < 200; j++){for (int k = 0; k < 10; k++){step[i][j][k] = 10000;}}}used[start.row][start.col][start.hp] = 1;step[start.row][start.col][start.hp] = 0;q.push(start);
}node moveto(node u, int dire)
{//最开始的问题出在没有给u,v生命值node v;v.row = u.row + mover[dire];v.col = u.col + movec[dire];//row写为colv.hp = u.hp;if (garden[v.row][v.col] == 'w'){v.hp = u.hp - 1;}return v;
}bool canmove(node u, int dire)
{node v;v = moveto(u, dire);if (v.row >= 0 && v.row < m && v.col >= 0 && v.col < n && v.hp > 0 && used[v.row][v.col][v.hp] == 0){return true;}return false;
}int bfs()
{while (!q.empty()){//取出结点unode u, v;u = q.front();q.pop();//扩展ufor (int i = 0; i < 4; i++){if (canmove(u, i)){v = moveto(u, i);if (v.row == target.row && v.col == target.col){return step[u.row][u.col][u.hp] + 1;}used[v.row][v.col][v.hp] = 1;step[v.row][v.col][v.hp] = step[u.row][u.col][u.hp] + 1;q.push(v);}}}return -1;
}int main()
{input();init(start);int answer;answer = bfs();cout << answer << endl;
}/*
下面是测试数据
4 4 2
w!ww
**ww
www+
****6
4 4 2
w!ww
*www
www+
****-1
*/

经过这两个题目的讲解,相信你对于广搜的理解应该更深刻了吧,模板都是一样的,好玩的广搜题目

下面我来看第二个题目的升级版

noj1653 僵尸又来了

题目描述

由于聪明的佳佳很善于布置花园,所以僵尸没能在天亮之前冲到佳佳家里,这次僵尸又要来佳佳家做客了,佳佳很高兴,因为姑妈送给佳佳的大嘴花派上了用场。你拿到了花园的地图(以二维矩阵的形式表示)以及起点和佳佳家的位置。花园里一个位置有大嘴花,僵尸到达时会被吃掉,同时从起点又会出来一个新的僵尸,如果僵尸到达大嘴花的位置时,前一个僵尸还没有吃完,大嘴花将被吃掉,看看僵尸能否在天亮前到达佳佳家里?能的话,最少花费多少时间。

题目要求及样例

输入:
输入的第一行包含4个整数:m,n,t, p。代表m行n列的地图、大嘴花吃掉僵尸花费的时间t和距离天亮的时间p。m,n都是小于200的正整数,t是小于10的正整数,第2行开始的m行是m行n列的花园地图,其中!代表起点,+表示佳佳家。*代表通路,@代表大嘴花,#表示墙。

输出:
输出僵尸到达佳佳家最少需要花费的时间。如果在天亮之前无法到达,则输出-1。

输入样例:

4 4 5 50

#!##
**##
**#+
***@

输出样例:

-1

#include<iostream>
#include<queue>using namespace std;struct node {int row;int col;
};node start, target;//记录结点
int m, n, t, p;//大嘴花
char  garden[200][200];//二维数组【花园】
queue<node> q; //结点队列
int used[200][200];
int step[200][200];//和之前一样使用记录步数int mover[4] = { -1,0,+1,0 };//上左下右
int movec[4] = { 0,-1,0,+1 };void input()//忘记吃掉第一行的回车
{cin >> m >> n >> t >> p;cin.get();for (int i = 0; i < m; i++){for (int j = 0; j < n; j++){garden[i][j] = cin.get();if (garden[i][j] == '!'){start.row = i;start.col = j;}if (garden[i][j] == '+'){target.row = i;target.col = j;}}cin.get();}
}void init(node start)
{for (int i = 0; i < 200; i++){for (int j = 0; j < 200; j++){step[i][j] = 10000;}}used[start.row][start.col] = 1;step[start.row][start.col] = 0;q.push(start);
}node moveto(node u, int dire)
{node v;v.row = u.row + mover[dire];v.col = u.col + movec[dire];//row写为colreturn v;
}bool canmove(node u, int dire)
{node v;v = moveto(u, dire);if (v.row >= 0 && v.row < m && v.col >= 0 && v.col < n && garden[v.row][v.col] != '#' && used[v.row][v.col] == 0){return true;}return false;
}int bfs()
{//想法是将结点记录,将时间记录,在时间*2层压入结点node temp;int tempt = 10000; //初始化while (!q.empty()){//取出结点unode u, v;u = q.front();q.pop();//扩展ufor (int i = 0; i < 4; i++){if (canmove(u, i)){v = moveto(u, i);if (garden[v.row][v.col] == '@'){if (t <= step[u.row][u.col] + 1)continue;else //到达时间为再来一次{temp.row = v.row;temp.col = v.col;tempt = 2 * (step[u.row][u.col] + 1);//不符合广搜,那就不标记continue;//继续}}//if (step[u.row][u.col] + 1 == tempt){//加上这个结点step[temp.row][temp.col] = tempt;q.push(temp);}//if (v.row == target.row && v.col == target.col && step[u.row][u.col] + 1 < p){return step[u.row][u.col] + 1;}used[v.row][v.col] = 1;if (step[u.row][u.col] + 1 <= step[v.row][v.col])//由于大嘴花影响{step[v.row][v.col] = step[u.row][u.col] + 1;}q.push(v);}}//if (tempt != 10000 && q.empty()){step[temp.row][temp.col] = tempt;q.push(temp);}}return -1;
}int main()
{input();init(start);int answer;answer = bfs();cout << answer << endl;
}/*4 4 5 50
#!##
**#+
**@*
**#*
8
*/

可以把两个地方的判断插入放在一起

if((tempt != 10000 && q.empty()) || (step[u.row][u.col] + 1 == tempt))

广搜(bfs)模板太好用了!bfs详细讲解--超详细题目分析(持续更新题目----)【算法分享】相关推荐

  1. 简单深搜广搜基本模板

    简单搜索 DFS: 剪枝,条件 容易超时,超时后基本就是剪枝的问题/无限递归?,或者用广搜试试? 模板(自己的理解) int n,m;//一般输入的行列数/边界 int mov[4][2] = {1, ...

  2. 算法提高课-搜索-双向广搜 AcWing 190. 字串变换:bfs、双向bfs、queue和unordered_map

    题目分析 来源:acwing 分析: 双向广搜主要用在最小步数模型(也称状态图模型)里面,这里整个状态空间一般是指数级别的,用双向广搜可以极大地提高运行效率. 双向广搜,顾名思义,就是从起点和终点都进 ...

  3. Catch That Cow【广搜】

    Catch That Cow POJ - 3278 Farmer John has been informed of the location of a fugitive cow and wants ...

  4. 2019 拖拉机(双端队列广搜)

    1. 问题描述: 干了一整天的活,农夫约翰完全忘记了他把拖拉机落在田地中央了.他的奶牛非常调皮,决定对约翰来场恶作剧.她们在田地的不同地方放了 N 捆干草,这样一来,约翰想要开走拖拉机就必须先移除一些 ...

  5. 深搜+回溯+广搜小结

    深搜 按照一定的顺序和规则,一直往深处走,直到走不通再返回,换一种路径重复上述步骤. 深搜一般可以找到问题的所有答案,但问题规模较大时,解集树的深度就会比较大并且比较宽,时间复杂度就会较高.与广搜相比 ...

  6. 算法学习 (门徒计划)3-3 深搜(DFS)与广搜(BFS)及经典问题 学习笔记

    算法学习 (门徒计划)3-3 深搜(DFS)与广搜(BFS)及经典问题 学习笔记 前言 深搜与广搜 搜索的核心概念 问题求解树 搜索剪枝和优化 问题求解树的状态 对比深搜与广搜 DFS-深度(deep ...

  7. 【图论专题】BFS中的双向广搜 和 A-star

    双向广搜 AcWing 190. 字串变换 #include <cstring> #include <iostream> #include <algorithm> ...

  8. 45行代码AC_2017年第八届蓝桥杯C/C++ A组第二题(广搜模板+解题报告)

    问题描述 有9只盘子,排成1个圆圈. 其中8只盘子内装着8只蚱蜢,有一个是空盘. 我们把这些蚱蜢顺时针编号为 1~8   每只蚱蜢都可以跳到相邻的空盘中, 也可以再用点力,越过一个相邻的蚱蜢跳到空盘中 ...

  9. 深搜DFS\广搜BFS 图初步入门

    首先,不管是BFS还是DFS,由于时间和空间的局限性,它们只能解决数据量比较小的问题. 深搜,顾名思义,它从某个状态开始,不断的转移状态,直到无法转移,然后退回到上一步的状态,继续转移到其他状态,不断 ...

最新文章

  1. java集成redis集群_spring集成redis cluster详解
  2. CSS对 网页进行布局
  3. 数组内容转qstring_用Qstring给char[]数组赋值(转)
  4. ubuntu16.04版本 the system is running in low-graphics mode
  5. 男人的快乐可以多简单?
  6. 【SPFA】【最短路/次短路】GF打Dota
  7. 2021-07-27 详解TCP连接建立和释放的过程(三报文握手和四次挥手)
  8. 网页关键词监控采集器-编辑必备
  9. 转贴一篇不错的Python入门教程 - Instant Hacking[译文]
  10. 发现一个HTML Form提交的小问题,不知道大家是不是都注意到了
  11. 16S多样性组成谱研究,9.13分的Water Research轻松二连发!
  12. Matlab 绘制柱状图并标注对应数字值
  13. DevOps元素周期表
  14. html是l面包屑效果,CSS制作面包屑
  15. 如何使用JavaScript检查数字是否为素数?
  16. 稀疏表示和DOA估计的联系与区别(the link and the gap)
  17. 无法初始化Qt平台插件
  18. error: ‘integer_sequence’ is not a member of ‘std’
  19. 微信code been used引发的一系列问题
  20. 用C语言计算个人所得税

热门文章

  1. Launch文件的学习:
  2. 基于TF-IDF与KMeans的海量新闻文本聚类
  3. 《机器学习》阅读心得——十一、特征选择和稀疏学习
  4. embed autopaly=false失效
  5. python中集合的长度_Python 集合(Set)
  6. J-001 Jetson硬件电路设计概论--NANO XAVIER NX
  7. 如何实现简单的多用户数据库存储
  8. 机器学习9:关于pytorch中的zero_grad()函数
  9. 03-Anaconda软件的介绍安装教程
  10. Beautiful Numbers