入门教程 高级搜索

高级搜索

信息

题解进度

Solved#TitleEditorial
SolvedA【ICPC 2004 上海站】【题解】The Rotation Game
B【ICPC 2006 横滨站 日本】
C【SCOI 2005 四川】
D【HDU 2006 校赛】
E【ICPC 2001 坎普尔站 印度】
F【ICPC 2001 大田站 韩国】
G【ICPC 1997 乌尔姆站 德国】
SolvedH【ICPC 1996 乌尔姆站 德国】一般搜索 双向BFS【题解】Knight Moves
SolvedI【HDU 2011 校级赛】奇怪搜索 双向BFS【题解】Nightmare Ⅱ
SolvedJ【ICPC 1998 SCUSA站】八数码 IDA*【题解】Eight
SolvedK【HDU 2017 暑期多校】次短路 A*【题解】Two Paths
SolvedL【ICPC 2017 沈阳站 网络赛】K短路 A*【题解】Made In Heaven
SolvedM【CCPC 2019 网络赛】不定点K短路 想法题【题解】path

通知栏

3月30-4月5日这周的新生训练准备训练高级搜索,就是讲双向广度搜索,迭代加深、A*算法,IDA*,你能负责安排吗?

第七周 高级搜索

一、视频:

1)搜索相关:

https://github.com/luoyongjun999/code/tree/master/%E8%A1%A5%E5%85%85%E8%B5%84%E6%96%99

2)A*:

https://www.bilibili.com/video/BV1D4411X71L

3)IDA*:
https://www.bilibili.com/video/BV1K4411R78Y

// 看前面一段简介就差不多了

// 可能去网上看博客会有更多理解

二、专题训练赛:

https://vjudge.net/contest/363902

// 可能会考虑陆续更一波题解

三、综合训练赛:
https://vjudge.net/contest/363899

双向广度搜索 (Bidirectional BFS)

这个其实是BFS的优化技巧,可以证明,从两个起点开始BFS,比单点开始要优化很多。

所有BFS过的题,你都可以写成双向的(首先看题目时间卡不卡,其次看实现麻不麻烦)。

  • https://www.geeksforgeeks.org/bidirectional-search/

伪代码

	BFS (queue_$){
        q.swap(queue)          //一个关键
        while (!q.empty()) {
            从q取出队首点
            
            if 遇到另一个点集:
            	return 有解,距离
            
            for () {
                 dr := r + d
                 dc := c + d
                判断合法性
                新点插入 queue_$ //另一个关键
       			更新标记数组,vis,dist
            }
        }
        return 无解
    };

	BiBFS(){
        res 
        queue_s	
        queue_t	
        分别插入对应的起点
        更新标记数组,vis,dist
            
        while 两者不都为空 :
        	// 这里有点启发式的思想,每次更新点集更小的队列
            if s的状态点数多于t,且queue_t非空 :
                res = BFS(queue_t);
            else :
                res = BFS(queue_s);
        
            if (有解且相遇) return res;
        }
        return 无解
    };

BBFS相关习题


【题解】Knight Moves

hdu 1372 - Knight Moves

题意:

就问你从棋盘上一个位置到另外一个位置的最短路。

思路:

我觉得你只要看懂了上面的伪代码的话,应该就能写了。

甚至如果你厉害的话,普通搜索也能过(不确定

[我的代码 1372 BBFS.cpp](https://github.com/TieWay59/HappyACEveryday/blob/master/2020codes/hdu/1372 BBFS.cpp)


【题解】Nightmare Ⅱ

hdu 3085 - Nightmare Ⅱ

模型是方格地图,有两个人M和G要见面,每个人只能四向移动。其中M可以一单位时间移动三次,G可以一单位时间移动一次。图上一开始有两个Z,这个Z每个单位时间会分裂,占满曼哈顿距离为2的所有位置。Z可以覆盖X,但是MG不可以走到XZ。每一时刻M或G移动前,看作Z已经分裂完毕。注意,题意隐含:MG被Z覆盖也会无法行动。

思路:这个提示也不算传统的宽搜,不是很入门,可能还有点入坟。

  • 一开始我以为,M的移动是可以确定的,于是把它的移动设成一个dir数组来判。后来才意识到,M移动的过程中三次位移每一次都不能落在Z上,那么如果是有个第二次落脚的位置恰变成Z了,那么后续的第三个位置是M达不到的。这告诉你,这个题非常的有鬼。
  • 由于G和Z都是单位时间变化的,M又有移动的特殊性,整体做法还是以双向宽搜为主,但是,M的每次移动都要单独拿出来判,具体地说就是要有三次while(!queue.empty())给M点判移动。
  • 然后,由于人必须是同一个单位时间内相遇,而且每个时间人都必须移动,所以这不是常规的最短路问题,而是枚举单位时间,更新所有上个时刻的位置,到下个位置,如果可以到达,如果在这个单位时间内,M首次走到G经过的位置(反之等价),可以说这就是相遇的最少时间。

我的代码 3085 Bidirectional BFS.cpp

迭代加深 ID(Iterative Deepening), IDDFS

这个算法代码上只是DFS稍微添加点东西而已。

迭代加深搜索,实际上就是做很多次深搜,同时逐渐提高每次深搜的深度限制。直到找到答案就结束。

可以证明,这样做可以在时间上接近BFS,在空间上接近DFS,可以避免两种基本搜索算法的极端。

在目标不深,分支很多的情况下,运行表现比较好。

A* (A_star)

这个算法的思想其实也不复杂,但是代码细节多点。

A星算法的启发性在于规定一个估价函数,来猜想一个点到目标的后续花费。

可以是不准的猜想,比如在方格模型中,用曼哈顿距离计算。

在代码上的表现,常常和bfs,反向图,最短路有关。

于是**“后继节点”就可以通过“已有花费+可能的后续花费”**的某个函数来比较。

特别的是,这个算法不太用在算法竞赛,而在游戏开发中更有作用。

第K短路问题

就是求某个点到某个点的第k短的路径长度。(我们所说的最短路一般就是K=1的情况)

放在这里讲也是因为有一种Astar的简单做法。

Astar做法的K短路不算太难,每一步都很清晰:

  1. 首先建立反向图(就是所有边和你原来的图相反的图,无向图就免去这一步)
  2. 然后在反向图从终点开始做一遍单源最短路(可以时dijkstra)
  3. 从起点开始做优先队列优化的BFS。
  4. Astar的体现在于优先队列中的节点的优先级和用启发函数要有关。
  5. 也就是按照f(u)=dis_start(u)+dis_final(u)从小到达排序。
  6. 当第K次遇到终点的时候,当前的距离就是K短路。

是不是看起来很暴力?确实很暴力,记住这样做的最坏复杂度是 O ( K N l o g ( N ) ) O(KNlog(N)) O(KNlog(N))的。

  • https://oi-wiki.org/graph/kth-path/
  • 世界上有很低复杂度的科技 O ( N l o g ( N ) + M l o g ( M ) + K l o g ( K ) ) O(Nlog(N)+Mlog(M)+Klog(K)) O(Nlog(N)+Mlog(M)+Klog(K)),但是代码很长很长,并且很难理解:https://www.isi.edu/natural-language/people/epp-cs562.pdf https://www.isi.edu/natural-language/people/epp-cs562.pdf

K短路相关习题


【题解】Two Paths

hdu 6181- Two Paths

题意:

这个题是给你无向图要你求次短路,也就是K=2的情况。

比较一般吧,而且也有别的做法(改写dijkstra即可)

小心处理爆int的问题。

[我的代码 6181 K短路(边权ll).cpp](https://github.com/TieWay59/HappyACEveryday/blob/8e937dc620db56668f565d1e38a337e40a6f4903/2020codes/hdu/6181 K短路(边权ll).cpp)


【题解】Made In Heaven

计蒜客 A1992 - Made In Heaven

题意:

这是一般的询问K短路,这个题比较重要,是沈阳网络赛的题目。

给大家唠叨两句,我们集训队暑期队伍排名,都会参照网络赛比赛的排名的。

这个题看起来好像不能用普通的Astar做,实际上他题目给你了一个长度剪枝,要充份利用。

[我的代码 A1992 K短路 Astart 剪枝.cpp](https://github.com/TieWay59/HappyACEveryday/blob/8e937dc620db56668f565d1e38a337e40a6f4903/2020codes/jisuanke/A1992 K短路 Astart 剪枝.cpp)


【题解】path

hdu 6705 - path

题意:

有向图模型,q次询问整个图上任意源汇的第K短路。

q次询问很多,你当然要预处理出前max(k)的答案。

思路:

枚举所有路径是不可能的,我们期望的是,在最少的枚举次数下,枚举到尽量短的路径。

对于一条当前剩下路径中最短的路径 P c u r P_{cur} Pcur,设这个路径最后一条边为 P c u r : E ( u , i ) P_{cur}:E(u,i) Pcur:E(u,i) ,表示从 u u u出发的第 i i i短的边。

那么根据这条路径,下一条比这个路径长的路径可能有(假设存在):

  • P : E ( u , i + 1 ) P:E(u,i+1) P:E(u,i+1)
  • P c u r + E ( t o ( u , i ) , 0 ) P_{cur} + E(to(u,i),0) Pcur+E(to(u,i),0)

先不去提这两者怎么比较,以及存在性的问题。

假如我们每次拿到一条当前的最短路径,然后放回这两种可能的后继,是不是总能保证工作集合的完备,并且这个集合的体积不会很大。

于是可以总结出,路径节点的表示(长度,u,i ),以及转移过程。

剩下的问题是,初始情况应该是怎么样的。

我们要保证初始的最短路径的枚举集完备,并且后续路径不会重复。

答:就是所有点的出发的最短边的集合。(自己思考为什么)

具体的实现只需要会优先队列就可以写了,没有什么固定的章法。

[我的代码 6705 任意源汇K短路 想法.cpp](https://github.com/TieWay59/HappyACEveryday/blob/a13239ffeaaf5a3252e3aabdec1b158ad25f1674/2020codes/hdu/6705 任意源汇K短路 想法.cpp)

IDA* (Iterative deepening A*)

顾名思义,就是把上面连个算法结合起来的算法。

  • https://blog.csdn.net/xiaonanxinyi/article/details/97896085?depth_1-utm_source=distribute.pc_relevant_right.none-task&utm_source=distribute.pc_relevant_right.none-task
  • 不存在的:wiki

IDA*相关习题


【题解】The Rotation Game

hdu 1667 - The Rotation Game

题意:

给你一个滚动游戏的模型,8方向可以拉动数组循环滚动1格。问你把中间八个移动到成同一个数的状态的最短移动方案,如果有多解要输出字典序最小的解。

第二行要输出最终格局中间八个数是哪一种。

思路:

首先我们来看怎么转化成图论模型,也就是模拟的部分。

你可以把这个#每一行从左到右,然后从上到下,这样编号,一个格局就变成一个长度为24的数组,每个元素都属于[1,2,3]

数组变成简单结点的方法有很多,主要就是状压或者哈希。

我采用了哈希成一个4进制的大整数的做法。(实际上你可以优化成二进制的状压,给大家思考)

state = 0;
for (auto in:input)
    if ('0' <= in && in < '4')
        state = (state << 2) + int(in - '0');

然后状态怎么转移。你想每一次拉动数组,所有元素都朝着一个方向位移,首部的元素放到末尾。这个过程其实可以等价于,把第一个元素冒泡交换到最后一个。

所以你需要的是实现一个子操作,交换。根据上面的编号和状压的方式,这个函数不难实现。

    const auto digitSwap = [](ll &s, ll a, ll b) {
        a = 2 * (a - 1);
        b = 2 * (b - 1);
        ll x = (s >> a) & 3;
        ll y = (s >> b) & 3;
        s = s - (x << a) - (y << b)
            + (x << b) + (y << a);
    };

然后就是终点状态的表示。在这个问题中,我的思路是枚举最终中间的数字是什么(也就全1或者2,3)然后把其他数组位置填上0,哈希成一个终点状态,每次当前状态都跟这个节点比较即可。

有了以上的理解,你应该可以写出一个爆搜的做法了,但是这样会TLE,或者MLE。

怎么优化呢?

  • 考虑迭代加深,枚举深度去DFS,每次深度+1保证第一次遇到的答案是最短的。
  • 考虑一个启发(也就是启发函数h)你可以假设,中间八个位置,有几个和目标状态不一样,就是最少还差几步(当然看起来不一定,但这个考虑不会使得方案变差)这样你就能确定一个状态是否有可能在你枚举的步长内达到终点。

有了这两点优化,就是标准的IDA*的模样了。

稍微顺带一提,我的状态是用4进制表示的,其实你也可以通过每次修改起始状态优化成二进制。因为你想,如果你的最终状态中心是1,那么2和3的位置其实是对你搜索的过程没有意义的,都可以看成是0。这样可以进一步优化。

我的代码 1667 IDAstar.cpp


【题解】Eight

hdu 1043 - Eight

八数码问题其实是一个老掉牙的麻烦题,思维障碍主要有以下几点:

  • 无解的结论:可以证明,输入的数组(除了x以外)逆序对有奇数个,那么这个八数码是无解的。
  • 启发式估价函数:用每个数字(除了x),与目标位置的曼哈顿距离,求和来表示预期的代价。
  • 其他细节:对于有解的情况,移动步数不应该很多(不会上千),但是同一步数的方案可能会很多。
  • 采取搜索方案:BFS空间危险,DFS深度危险,那么就用迭代加深吧,好像还有估计函数,那就是IDA*了。

如果你已经懂了以上的思考点,并且了解了IDA*算法的框架,就可以开始敲了。

其实你不用把IDA*想的太复杂,也不需要去找模板,思路就是:你枚举限制DFS深度多次去DFS,中间用启发式估价函数剪枝就好了。这个题我的剪枝是:steps+h(state)>depth,这代表估计到预期的步数超过限制深度。

[我的代码 1043 IDAstar2.cpp](https://github.com/TieWay59/HappyACEveryday/blob/master/2020codes/hdu/1043 IDAstar2.cpp)

我还去研究了洛谷的另外一个八数码的题不输出方案,只要输出最小步数的。这个题的数据是卡A*的做法的。你看,这个算法多么尴尬,在一定情况下还很容易被卡掉。

话题之外

我来解释一下为什么要有这么复杂的算法。

以及为什么例题都那么——陈旧。

你可以先浏览一下这篇文章:A*,Dijkstra,BFS算法性能比较及A*算法的应用

上面这些花里胡哨,看似高级的搜索算法,还是在解决一个老掉牙的问题,最短路。

这里的“最短路”,不一定是图形上的最短路径,还可能是某个事物(首当其冲,滑块拼图)的最少次数之类的。

但是像Dijkstra在稠密的图,或者方格图上的表现,会逊色很多;还有一些抽象情况(滑块拼图)分支庞杂,无法建图。所以会存在像IDA*针对上述这样的情况更有效的算法。

但是,也是因为针对性太特殊了,导致这样的算法变不出太多的花样。不是说出不了难题,是很难弄出好题。而且同样的题可能用其他歪门邪道的方法也可以做出来,比如dfs巧妙剪枝,时间复杂度很难卡出那么多不同的优化的做法。

所以近五年很少有出这样的题,历史上也很难找到类似的题。

但作为准备者,我不敢保证说以后就不会有了,而且很可能这样的题目再次现身,会是以很难的题目出现的。(因为简单的基础题大家刷的越来越多了)

看起来,现代的搜索题,更多在花样上创新,而不会在老花样上吊胃口了。

点击查看源网页
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
前言 第一部分 JSP入门 第1章 概述 1.1 Java技术 1.1.1 Java技术的发展 1.1.2 JavaBeans 1.1.3 JDBC 1.1.4 J2EE 1.1.5 EJB 1.1.6 Java Servlet 1.2 JSP技术 1.2.1 JSP技术概述 1.2.2 JSP的优势及与其他Web开发工具的比较 1.3 JSP开发Web的几种主要方式 1.3.1 直接使用JSP 1.3.2 JSP+JavaBeans 1.3.3 JSP+JavaBeans+Servlet 1.3.4 J2EE开发模型 1.4 本书用到的软件及获取 第2章 预备知识 2.1 Java程序设计基础 2.1.1 Java语言规则 2.1.2 Java变量和函数 2.1.3 子类 2.1.4 this和super 2.1.5 类的类型 2.1.6 抽象类 2.1.7 接口 2.1.8 包 2.2 JavaBeans 2.2.1 JavaBeans的属性 2.2.2 JavaBeans的事件 2.2.3 持久化 2.2.4 用户化 2.3 Java Servlet 2.3.1 HTTP Servlet API 2.3.2 系统信息 2.3.3 传送HTML信息 2.4 SQL语言 2.4.1 SQL子类型 2.4.2 SQL语言的具体命令和使用 2.5 JDBC 2.5.1 什么是 JDBC 2.5.2 JDBC 产品 2.5.3 连接概述 2.5.5 一个简单的例子 第3章 JSP开发平台的建立:Tomcat 3.1 Tomcat的安装和直接使用 3.2 Tomcat和Apache的配合 3.3 Tomcat和IIS的配合 3.4 Tomcat的配置和常见问题 3.4.1 Tomcat的主配置文件:server.xml 3.4.2 Windows下代码保护的问题 3.4.3 Apache、IIS和Tomcat协作时工作 目录的添加 3.4.4 设定Tomcat作为Windows的服务而启动 3.4.5 在Tomcat中建立新的Web应用程序 第4章 JSP的语法和语义 4.1 通用的语法规则 4.1.1 元素的语法规则 4.1.2 JSP中的相对路径 4.2 注释 4.3 指令 4.3.1 page指令 4.3.2 include指令 4.3.3 taglib指令 4.4 内置对象 4.5 脚本元素 4.5.1 声明 4.5.2 表达式 4.5.3 脚本代码 4.6 动作 4.6.1 id和scope属性 4.6.2 标准动作 第5章 作为XML的JSP 5.1 为什么要使用XML相容的语法形式 5.2 关于文本类型的语法 5.2.1 jsp:root元素 5.2.2 公共标识符 5.3 指令 5.3.1 page指令 5.3.2 include指令 5.3.1 taglib指令 5.4 脚本元素 5.4.1 声明 5.4.2 脚本代码 5.4.3 表达式 5.5 如何将一个普通的JSP文件转换为一个XML 文档 5.6 JSP1.1的DTD文件 第6章 JSP基础实例 6.1 第一个JSP程序—HelloWorld! 6.2 注释的使用 6.3 脚本元素 6.3.1 声明 6.3.2 表达式 6.3.3 脚本代码 6.4 page指令 6.4.1 import 6.4.2 session 6.4.3 错误处理 6.5 包含其他文件 6.6 使用JavaBean 6.7 内置对象 6.7.1 用request对象获取客户端的数据 6.7.2 用response对象向客户端发送信息 6.7.3 其他内置对象 6.8 6.9 使用插件 6.10 使用session对象 6.10.1 会话的概念 6.10.2 session对象可用的方法和属性 6.10.3 session对象的基本例子 6.10.4 利用session制作一个购物车 6.10.5 Java
### 回答1: PADS是一款电路设计软件,在使用PADS软件进行电路设计时,需要掌握一些基础知识。在这个完全版的PADS基础入门教程中,将介绍PADS软件的基本操作和使用方法,让初学者能够快速入门。 首先,我们需要了解PADS软件的界面和基础操作。PADS软件的界面主要包含菜单栏、工具栏、编辑区、器件管理区、元件库管理区、工程管理区等几个基本部分。在PADS软件中,我们可以通过使用快捷键或鼠标进行操作。 其次,我们需要熟悉PADS软件中的元件库管理和器件管理。在PADS软件中,元件库是一个有组织的、可搜索的部件库,包含所有用于设计电路的标准芯片、传感器、电容、电阻、连接器等元器件。器件管理是指根据设计需求对元器件进行管理和使用。 接着,我们需要学习PADS软件中的电路设计和布局。在PADS软件中,我们可以使用原理图编辑器创建电路图,并对电路图进行模拟。同时,PADS软件还支持进行电路布局的设计和编辑。在进行布局设计时,需要根据电路规划和排布要求进行布局设计,同时还需要考虑到元器件的尺寸和间距等因素。 最后,我们需要了解PADS软件的PCB设计和布线技巧。在PADS软件中,我们可以使用PCB编辑器进行PCB布局和设计,同时还可以进行布线和调整。在进行布线时,需要根据信号传输特性和布线规则进行设计。同时,还需要保证电路的可靠性和稳定性。 总之,PADS基础入门教程是一项重要的学习任务,对于电路设计初学者来说,是必不可少的。通过掌握PADS软件的基本操作和使用方法,可以提高电路设计的效率和质量。 ### 回答2: Pads是一款电子设计自动化软件,它在PCB设计的领域具有很强的功能和应用。下面将为大家介绍Pads的基础入门教程完全版。 1、Pads软件的安装和基本界面介绍。首先需要下载Pads软件并进行安装,然后打开软件,进入到主界面。在主界面中,Pads的工作区域被分成了几个分区,包括功能区、工程区、编辑区、结果区等。 2、创建一个新的工程,并介绍如何导入原理图。使用Pads进行PCB设计,首先需要在Pads软件中创建一个新的工程,然后在工程中导入原理图。可以通过“File”菜单下的“Open”命令打开一个原理图文件,然后通过“File”菜单下的“Import”命令将原理图导入到工程中。 3、进行PCB布局与布线。在Pads软件中进行PCB布局和布线,可以选中需要的组件,然后通过鼠标将它们放置到相应的位置。在进行布线时,可以通过布线工具来将不同的元件相互连接。同时,还需要注意到布线的合理性和阻抗匹配。 4、进行设计规则的设置和检查。在Pads软件中进行PCB设计时,需要进行设计规则的设置和检查。通过“Design Rules”命令可以设置不同的规则,并在设计时进行规则检查,以保证PCB设计的正确性和稳定性。 5、PCB输出和制造。完成PCB设计后,需要进行输出和制造。在Pads软件中可以通过“File”菜单下的“Export”命令进行输出,生成Gerber文件,然后可以将其进行制造。同时,在输出时还需要注意到线路板金手指和排针的设置,以及铜皮厚度等参数的设置。 通过以上的五个步骤,可以基本掌握Pads的基础入门技巧,并可以进行简单的PCB设计。当然,在实际应用中还需要进一步学习和掌握高级技巧,以便更好地进行电路设计和PCB制造。 ### 回答3: Pads是一款PCB设计软件,常用于电路板制作。以下是Pads基础入门教程完全版。 1. 实验环境配置 Pads软件需要在Windows操作系统中运行,可在Mentor Graphics官网下载安装,注册后才能进行使用。 2. 库文件的创建和使用 在Pads软件中,库文件是图形和文本元素的集合,可用于电路图导入、设计和布局。制作并导入库文件需要使用Pads Layout软件。 3. 元件库信息 元件库信息在Pads软件中起着至关重要的作用,它包括元件名称、标准、值和尺寸等参数。元件库还可以添加自定义元件和修改现有元件。 4. PCB设计原理图 PCB设计原理图是Pads软件中电路板设计的基础,它是电路板的重要组成部分,用于设计电路图和验证设计。 5. PCB板尺寸和层定义 PCB板尺寸和层定义决定了设计的PCB板的大小和多少层,它将直接影响到PCB板的性能。 6. PCB贴片布图设计 在PCB贴片布图设计中,需要定义贴片元件的位置、方向和焊盘等信息,以确保电路板的正确性和可靠性。 总之,掌握Pads软件的基本原理和基础知识,可以帮助用户设计出高性能、高可靠性的电路板。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值