数据结构实验之图论四:迷宫探索_迷宫搜索类的双向bfs问题(例题详解)

前言

文章若有疏忽还请指正!

更多精彩还请关注公众号:bigsai

头条号:一直码农一直爽

在搜索问题中,以迷宫问题最具有代表性,无论是八皇后的回溯问题,还是dfs找出口,bfs找最短次数等等题目的问题。在我们刚开始ac的时候、可能有着很多满足感!感觉是个迷宫问题咱么都可以给他这么搜出来 !!

501d1651a22141c091850d0530f9818e

各种TLE(超时),不分析原因还会一直提交一直TLE


然而,当数据达到一定程度,我们使用简单的方法肯定会爆炸的,*。就可能需要一些特殊的巧妙方法处理,比如各种剪枝、优先队列、A*、dfs套bfs,又或者利用一些非常厉害的数学方法比如康托展开(逆展开)等等。而今天,我们谈谈双向bfs*。

98f9f3d4d6e0c070c572036ec7ed09da.png

bfs类问题

bfs又称广度优先搜索

  • 估计大部分人第一次接触bfs的时候是在学习数据结构的二叉树的层序遍历!借助一个队列一层一层遍历。
  • 第二次估计就是在学习图论的时候,给你一个图,让你写出一个bfs遍历的顺序。

此后再无bfs…

而很多笔试面试还是其他机试其实对bfs的要求远远不止那么低的,需要能够处理一些小问题、写出对应代码。而事bfs可以处理很多问题,很多dfs搜索能够解决的问题bfs也能解决很多(相反也成立),并且很多跟状态有些关系的用bfs更好控制,因为bfs借助的是一个队列实现,队列中储存节点就可以保存一些节点的状态。

036e9e7a92b95aab7af4f6b4c8aeba04.png


不过bfs并不是万能的,具体问题要看迷宫的大小的,迷宫长宽没增加一个数,那么这个数量级增加是非常大的,因为搜索次数大概和边长的指数级别有关系。当然这里不详细介绍bfs了,大家可以看以前的一篇文章。数据结构与算法—图论之dfs、bfs(深度优先搜索、宽度优先搜索)。

双向bfs

什么样的情况可以使用双向bfs来优化呢?其实双向bfs的主要思想是问题的拆分吧,比如在一个迷宫中可以往下往右行走,问你有多少种方式从左上到右下。

  • 正常情况下,我们就是搜索遍历,如果迷宫边长为n,那么这个复杂度大概是2^n^级别.
  • 但是实际上我们可以将迷宫拆分一下,比如根据对角线(比较多),将迷宫一分为二。其实你的结果肯定必然经过对角线的这些点对吧!我们只要分别计算出各个对角线各个点的次数然后相加就可以了!
  • 怎么算? 就是从(0,0)到中间这个点mid的总次数为n1,然后这个mid到(n,n)点的总次数为n2,然后根据排列组合总次数就是n1*n2(n1和n2正常差不多大)这样就可以通过乘法减少加法的运算次数啦!
  • 简单的说,从数据次数来看如果直接搜索全图经过下图的那个点的次数为 n1 * n2 次,如果分成两个部分相乘那就是n1+n2次。两者差距如果n1,n2=1000左右,那么这么一次差距是平方(根号)级别的。从搜索图形来看其实这么一次搜索是本来一个n*n大小的搜索转变成n次(每次大概是(n/2) * (n/2)大小的迷宫搜索两次)。也就是如果18*18的迷宫如果使用直接搜索,那么大概2^18次方量级,而如果采用双向bfs,那么就是2^9这个量级。
03fdc8ef2aa8acab87a7c403710e2345.png

例题实战

题目链接:http://oj.hzjingma.com/contest/problem?id=20&pid=8#problem-anchor

6c2c2c2444e439a5f28fb0f60811bd1c.png
c60238c7b1e380d8405d56c2c0f033ca.png


分析:对于题目的要求还是很容易理解的,就是找到所有的路径种类,再判断其中是对称路径的有几个输出即可!

对于一个普通思考是这样的,首先是进行dfs,然后动态维护一个字符串,每次跑到最后判断这个路径字符串是否满足对称要求,如果满足那么就添加到容器中进行判断。可惜很遗憾这样是超时的,仅能通过40%的样例。

接着用普通bfs进行尝试,维护一个node节点,每次走的时候路径储存起来其实这个效率跟dfs差不多依然超时。只能通过40%数据。

接下来就开始双向bfs进行分析

  • 既然只能右下,那么对角线的那个位置的肯定是中间的那个字符串的!它的存在不影响是否对称的(n*n的迷宫路径长度为n-1 + n为奇数).
  • 我们判断路径是否对称,只需要判断从(1,1)到对角节点k(设为k节点)的路径有没有和从(n,n)到k相同的。如果有路径相同的那么就说明这一对构成对称路径
  • 在具体实现上,我们对每个对角线节点可以进行两次bfs(一次左上到(1,1),一次右下到(n,n)).并且将路径放到两个hashset(set1,set2)中,跑完之后用遍历其中一个hashset中的路径,看看另一个set是否存在该路径,如果存在就说明这个是对称路径放到 总的hashset(set) 中。对角线每个位置都这样判断完最后只需要输出总的hashset(set)的集合大小即可!

ac代码如下:

import java.util.ArrayDeque;import java.util.HashSet;import java.util.Queue;import java.util.Scanner;import java.util.Set;public class test2 {        static class node{         int x;         int y;        String path="";        public node() {}        public node(int x,int y,String team)        {            this.x=x;            this.y=y;            this.path=team;        }    }    public static void main(String[] args) {        Scanner sc=new Scanner(System.in);        Setset=new HashSet();//储存最终结果        int n=Integer.parseInt(sc.nextLine());        char map[][]=new char[n][n];        for(int i=0;iq1=new ArrayDeque();//左上的队列        Queueq2=new ArrayDeque();//右下的队列        for(int i=0;iset1=new HashSet();//储存zuoshang            Setset2=new HashSet();//储右下            q1.add(new node(i,n-1-i,""+map[i][n-1-i]));            q2.add(new node(i,n-1-i,""+map[i][n-1-i]));            while(!q1.isEmpty()&&!q2.isEmpty())            {                node team=q1.poll();                node team2=q2.poll();                if(team.x==n-1&&team.y==n-1)//到终点,将路径储存                {                    //System.out.println(team2.path);                       set1.add(team.path);                    set2.add(team2.path);                }                else {                    if(team.x0)//上                    {                        q2.add(new node(team2.x-1, team2.y, team2.path+map[team2.x-1][team2.y]));                    }                    if(team2.y>0)//左                    {                        q2.add(new node(team2.x, team2.y-1, team2.path+map[team2.x][team2.y-1]));                    }                }            }            for(String va:set1)            {                if(set2.contains(va))                {                    set.add(va);                }            }        }        System.out.println(set.size());         }}
c6fed3d9fd8d8bfcc9cdf9f7f3ba0329.png

如果觉得还行,欢迎转发、点赞、关注哈!

e231b80cc6c0e7a8db13abe79e7bb0b0.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338884.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ejb运行程序_在哪里可以运行EJB?

ejb运行程序EJB不仅为Java EE应用服务器而建。 他们还可以执行在的Java EE Web Profile的容器或简单的Java SE环境(Java SE的啊- !E!ASY 的J ava 乙是指合同)。 EJB Lite 仅支持完整的EJB规范提供的功能的子集所必需的-相同功能的…

css vue 选项卡_vuejs实现标签选项卡动态更改css样式的方法

html{{item.name}}jsvar app new Vue({el:"#app",router,data:{m:"hello vue.js",active:2,idx:0, //默认选择首页headerList:[{name:首页1},{name:首页2},{name:首页3},{name:首页4},{name:首页5},{name:首页6}]},methods:{// 1.选择selectMainTheme:fun…

ngrok服务器搭建_利用暴露在外的API,无法检测的Linux恶意软件将矛头指向Docker服务器...

K8s已经成为一线大厂分布式平台的标配技术。你是不是还在惆怅怎么掌握它?来这里,大型互联网公司一线工程师亲授,不来虚的,直接上手实战,3天时间带你搭建K8s平台,快速学会K8s,点击下方图片可了解…

干货 | C语言系列3——常量,运算符,常用数学函数......

符号常量和const常量1.符号常量符号常量通俗来讲就是“替换”,又称为宏定义。格式如下:#define 标识符 常量宏定义可以定义单个变量为常量,也可以定义某个语句或片段。宏定义有一点需要特别注意,它只是“机械”替换,并…

javafx 浏览器_浏览器中的JavaFX

javafx 浏览器浏览器中的JavaFX屏幕截图 最近,Carl Dea和我启动了一个新项目,将JavaFX 8引入浏览器。 今天,我想介绍我们创建的前两个概念验证,以查看该想法是否完全可行。 对于不耐烦的人,这里是到PoC的链接。 但请注…

django写mysql轮询_django 多数据库及分库实现方式

定义及路由机制定义在settings里面的DATABASES是一个字典,用于定义需要的数据库,如下,一共定义了两个数据库。DATABASES {default: {NAME: app_data,ENGINE: django.db.backends.postgresql_psycopg2,USER: postgres_user,PASSWORD: s3krit}…

python换行不执行_PYTHON无法换行问题,求指点

PYTHON无法换行问题,百思不得其解。fp.write(u{a}\n.format(a\n.join(a)))改成fp.write(u{a}\n.format(a,.join(a)))“,”可以正常显示下附源码,请指点一二#!/usr/bin/env python# encodingutf-8from bs4 import BeautifulSoupimp…

javafx 示例_JavaFX列表示例

javafx 示例这是使用JavaFX构建的示例列表应用程序。 该应用程序是待办事项列表。 该应用程序具有添加,更新和删除列表中项目的功能。 列表数据存储在HSQLDB关系数据库中。 该应用程序使用JDBC(Java数据库连接)API访问数据库。 该应用程序打包…

嵌入式C语言的7个硬核知识

1void 与 void*void表示的是无类型,不可以采用这个类型声明变量或常量,但是可以把指针定义为void类型,如void* ptr。void指针可以指向任意类型的数据,可用任意数据类型的指针对void指针赋值,比如int *ptrInt&#xff1…

python离线安装第三方库whl_详细说明如何在pycharm不联网的情况下,离线安装第三方库及依赖包(如sklearn)...

1、安装目标库1、首先,选择你要导入的库文件,如seaborn库下载网站:https://pypi.org/或https://www.lfd.uci.edu/~gohlke/pythonlibs/#scipy2、在下载路径下空白处,按住Shift鼠标右键,选择在此处打开命令窗口执行安装命…

java 手机网站 cookie操作_java操作cookie问题

做的一个ssh项目,其中需要使用cookie来辅助记录用户页面访问量。可是我在使用的时候出现问题,就是我设置了有效期,但是关闭浏览器后或者重新打开另一个浏览器均不能获取到设置的coo...做的一个ssh项目,其中需要使用cookie来辅助记…

C语言指针用得好犹如神助!这些使用技巧值得收藏

指针用的好犹如神助,用不好会让你叫苦连连,但大多数人是用不好指针的,所以后来的很多语言都把指针封装,屏蔽。比如JAVA,java是没有指针的,但是很多地方都用到指针,不过不对用户开放,…

wso2_使用WSO2开发

wso2几个月以来,我又开始使用WSO2产品。 在接下来的文章中,我描述了我遇到的一些(小)问题以及如何解决它们。 设置开发环境时,我要做的第一件事是在Mac上下载Developer Studi o(64位版本)。 解…

python数据获取手段包括哪些_python开发应用-本地数据获取方法

文件的打开、读写和关闭文件的打开:file_objopen(filename,moder,buffering-1,...)filename是强制参数mode是可选参数,默认值是rbuffering是可选参数,默认值为-1(0代表不缓冲,1或大于1的值表示缓冲一行或指定缓冲区大小)f1open(e:…

有意思的C语言运算符

在C语言中,运算符用于执行程序代码运算,会针对两个或者两个以上操作数进行运算。比如:5 - 2,它的操作数是 5 和 2,而运算符则是 “-”。常见的运算符可大致分为 4 种类型:算术运算符、关系运算符、赋值运算…

java反向映射_opencv 直方图和直方图反向映射

histogram使用np进行直方图像素统计通过histogram对图像的三个通道分别进行一维直方图统计,由于被统计的数组必须是一维的,所以使用ravel将二维数组转换为一维数组,通过range参数指定统计区间为0--256,bin参数指定将统计区间分为2…

hamcrest_重新设计Hamcrest

hamcrest我在Hamcrest库上做了几篇文章 ,我确实很喜欢使用它,但是我希望对其进行一些更改。 我了解他们所做的大多数设计决策,但我认为其中一些确实不值得。 介绍Litecrest 我对库所做的大多数更改都有助于减轻Hamcrest的负担,因…

程序员鄙视链python_关于程序员之间的鄙视链

鄙视像条食物链,是个绕不开的怪圈。在这个怪圈中,每一个人,都在链条的最末端。朋友圈有鄙视链,相亲有鄙视链,程序员圈也有鄙视链看完下面的鄙视链,你是那一层?↓↓↓↓【1】努力成为全栈&#x…

java自定义jsp标签_自定义JSP标签

1 写一个标签处理类//标签处理类public class IpTag implements SimpleTag {private PageContext pageContext;//Web容器调用NO1public void setJspContext(JspContext pc) {System.out.println("setJspContext()");pageContext (PageContext) pc;}//Web容器调用NO2…

新手学习C语言编程的8个致命错误,你中招了吗?

序言C语言的最大特点是:功能强、使用方便灵活。C编译的程序对语法检查并不象其它高级语言那么严格,这就给编程人员留下“灵活的余地”。但由于这个灵活给程序的调试带来了许多不便,尤其对初学C语言的人来说,经常会出一些连自己都不…