时空优化算法
❶ 计算机研究生方向,具体解释一下计算机应用技术中的方向是什么
计算机应用方向是一个很宽泛的概念,里面有非常非常多的小方向,不同学校、导师的方向也不一样。一般计算机应用方向有下面几个小方向:机器学习、最优化、模式识别、计算机图形学、机器视觉与图像处理、自然语言处理、人机交互、虚拟现实……这些方向大多都需要不错的编程能力和很好的数学功底(高数、线数、概率论、离散数学)
下面是我个人了解的方向的大概介绍:
1. 机器学习这个方向现在很火,如果学的很精通,毕业后能找到很不错的工作。这个方向需要很多数学知识(微积分、线性代数、概率论),也需要一定编程能力,至少能把自己的想法实现。这个方向下面有很多子方向,比如自然语言处理、模式识别、数据挖掘等
2. 最优化这个方向是用优化算法处理现有的问题,需要数值分析和一些逻辑的知识,也需要一定编程能力。
3. 计算机图形学包括一些小方向。有些研究CAD,这个不需要编程;有些研究自然景物的模拟(比如水流、头发、火焰),需要微积分、线性代数的知识,有时会用到流体力学的公式,这个方向需要熟悉一些图形接口(openGL、D3D)。
4. 图像处理这个方向需要一些信号处理方面的知识。
5. 剩下的还有比如人机交互、虚拟现实、机器视觉等,都是一些交叉的方向,设计一些方法解决现有的问题
如果具体还有什么问题,可以追问。
❷ 选择三种数学建模方法,介绍其内容并说明其适用的问题类型,并举例
摘要 对于大家来说,建模是大家觉得比较难的内容。那么如何进行有效的建模呢?今天,沪江小编就为大家分享几种常用的数学建模方法,一起来看看吧!
❸ 关于动态规划算法,哪位可以讲一下自己心得体会
动态规划的特点及其应用
安徽 张辰
动态规划 阶段
动态规划是信息学竞赛中的常见算法,本文的主要内容就是分析它的特点。
文章的第一部分首先探究了动态规划的本质,因为动态规划的特点是由它的本质所决定的。第二部分从动态规划的设计和实现这两个角度分析了动态规划的多样性、模式性、技巧性这三个特点。第三部分将动态规划和递推、搜索、网络流这三个相关算法作了比较,从中探寻动态规划的一些更深层次的特点。
文章在分析动态规划的特点的同时,还根据这些特点分析了我们在解题中应该怎样利用这些特点,怎样运用动态规划。这对我们的解题实践有一定的指导意义。
动态规划是编程解题的一种重要的手段,在如今的信息学竞赛中被应用得越来越普遍。最近几年的信息学竞赛,不分大小,几乎每次都要考察到这方面的内容。因此,如何更深入地了解动态规划,从而更为有效地运用这个解题的有力武器,是一个值得深入研究的问题。
要掌握动态规划的应用技巧,就要了解它的各方面的特点。首要的,是要深入洞悉动态规划的本质。
§1动态规划的本质
动态规划是在本世纪50年代初,为了解决一类多阶段决策问题而诞生的。那么,什么样的问题被称作多阶段决策问题呢?
§1.1多阶段决策问题
说到多阶段决策问题,人们很容易举出下面这个例子。
[例1] 多段图中的最短路径问题:在下图中找出从A1到D1的最短路径。
仔细观察这个图不难发现,它有一个特点。我们将图中的点分为四类(图中的A、B、C、D),那么图中所有的边都处于相邻的两类点之间,并且都从前一类点指向后一类点。这样,图中的边就被分成了三类(AàB、BàC、CàD)。我们需要从每一类中选出一条边来,组成从A1到D1的一条路径,并且这条路径是所有这样的路径中的最短者。
从上面的这个例子中,我们可以大概地了解到什么是多阶段决策问题。更精确的定义如下:
多阶段决策过程,是指这样的一类特殊的活动过程,问题可以按时间顺序分解成若干相互联系的阶段,在每一个阶段都要做出决策,全部过程的决策是一个决策序列[1]。要使整个活动的总体效果达到最优的问题,称为多阶段决策问题。
从上述的定义中,我们可以明显地看出,这类问题有两个要素。一个是阶段,一个是决策。
§1.2阶段与状态
阶段:将所给问题的过程,按时间或空间特征分解成若干相互联系的阶段,以便按次序去求每阶段的解。常用字母k表示阶段变量。[1]
阶段是问题的属性。多阶段决策问题中通常存在着若干个阶段,如上面的例子,就有A、B、C、D这四个阶段。在一般情况下,阶段是和时间有关的;但是在很多问题(我的感觉,特别是信息学问题)中,阶段和时间是无关的。从阶段的定义中,可以看出阶段的两个特点,一是“相互联系”,二是“次序”。
阶段之间是怎样相互联系的?就是通过状态和状态转移。
状态:各阶段开始时的客观条件叫做状态。描述各阶段状态的变量称为状态变量,常用sk表示第k阶段的状态变量,状态变量sk的取值集合称为状态集合,用Sk表示。[1]
状态是阶段的属性。每个阶段通常包含若干个状态,用以描述问题发展到这个阶段时所处在的一种客观情况。在上面的例子中,行人从出发点A1走过两个阶段之后,可能出现的情况有三种,即处于C1、C2或C3点。那么第三个阶段就有三个状态S3=。
每个阶段的状态都是由以前阶段的状态以某种方式“变化”而来,这种“变化”称为状态转移(暂不定义)。上例中C3点可以从B1点过来,也可以从B2点过来,从阶段2的B1或B2状态走到阶段3的C3状态就是状态转移。状态转移是导出状态的途径,也是联系各阶段的途径。
说到这里,可以提出应用动态规划的一个重要条件。那就是将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的发展,而只能通过当前的这个状态。换句话说,每个状态都是“过去历史的一个完整总结[1]”。这就是无后效性。对这个性质,下文还将会有解释。
§1.3决策和策略
上面的阶段与状态只是多阶段决策问题的一个方面的要素,下面是另一个方面的要素——决策。
决策:当各段的状态取定以后,就可以做出不同的决定,从而确定下一阶段的状态,这种决定称为决策。表示决策的变量,称为决策变量,常用uk(sk)表示第k阶段当状态为sk时的决策变量。在实际问题中,决策变量的取值往往限制在一定范围内,我们称此范围为允许决策集合。常用Dk(sk)表示第k阶段从状态sk出发的允许决策集合。显然有uk(sk) ?Dk(sk)。[1]
决策是问题的解的属性。决策的目的就是“确定下一阶段的状态”,还是回到上例,从阶段2的B1状态出发有三条路,也就是三个决策,分别导向阶段3的C1、C2、C3三个状态,即D2(B1)=。
有了决策,我们可以定义状态转移:动态规划中本阶段的状态往往是上一阶段和上一阶段的决策结果,由第k段的状态sk和本阶段的决策uk确定第k+1段的状态sk+1的过程叫状态转移。状态转移规律的形式化表示sk+1=Tk(sk,uk)称为状态转移方程。
这样看来,似乎决策和状态转移有着某种联系。我的理解,状态转移是决策的目的,决策是状态转移的途径。
各段决策确定后,整个问题的决策序列就构成一个策略,用p1,n=表示。对每个实际问题,可供选择的策略有一定范围,称为允许策略集合,记作P1,n,使整个问题达到最有效果的策略就是最优策略。[1]
说到这里,又可以提出运用动态规划的一个前提。即这个过程的最优策略应具有这样的性质:无论初始状态及初始决策如何,对于先前决策所形成的状态而言,其以后的所有决策应构成最优策略[1]。这就是最优化原理。简言之,就是“最优策略的子策略也是最优策略”。
§1.4最优化原理与无后效性
这里,我把最优化原理定位在“运用动态规划的前提”。这是因为,是否符合最优化原理是一个问题的本质特征。对于不满足最优化原理的一个多阶段决策问题,整体上的最优策略p1,n同任何一个阶段k上的决策uk或任何一组阶段k1…k2上的子策略pk1,k2都不存在任何关系。如果要对这样的问题动态规划的话,我们从一开始所作的划分阶段等努力都将是徒劳的。
而我把无后效性定位在“应用动态规划的条件”,是因为动态规划是按次序去求每阶段的解,如果一个问题有后效性,那么这样的次序便是不合理的。但是,我们可以通过重新划分阶段,重新选定状态,或者增加状态变量的个数等手段,来是问题满足无后效性这个条件。说到底,还是要确定一个“序”。
在信息学的多阶段决策问题中,绝大部分都是能够满足最优化原理的,但它们往往会在后效性这一点上来设置障碍。所以在解题过程中,我们会特别关心“序”。对于有序的问题,就会考虑到动态规划;对于无序的问题,也会想方设法来使其有序。
§1.5最优指标函数和规划方程
最优指标函数:用于衡量所选定策略优劣的数量指标称为指标函数,最优指标函数记为fk(sk),它表示从第k段状态sk采用最优策略p*k,n到过程终止时的最佳效益值[1]。
最优指标函数其实就是我们真正关心的问题的解。在上面的例子中,f2(B1)就表示从B1点到终点D1点的最短路径长度。我们求解的最终目标就是f1(A1)。
最优指标函数的求法一般是一个从目标状态出发的递推公式,称为规划方程:
其中sk是第k段的某个状态,uk是从sk出发的允许决策集合Dk(sk)中的一个决策,Tk(sk,uk)是由sk和uk所导出的第k+1段的某个状态sk+1,g(x,uk)是定义在数值x和决策uk上的一个函数,而函数opt表示最优化,根据具体问题分别表为max或min。
,称为边界条件。
上例中的规划方程就是:
边界条件为
这里是一种从目标状态往回推的逆序求法,适用于目标状态确定的问题。在我们的信息学问题中,也有很多有着确定的初始状态。当然,对于初始状态确定的问题,我们也可以采用从初始状态出发往前推的顺序求法。事实上,这种方法对我们来说要更为直观、更易设计一些,从而更多地出现在我们的解题过程中。
我们本节所讨论的这些理论虽然不是本文的主旨,但是却对下面要说的动态规划的特点起着基础性的作用。
§2动态规划的设计与实现
上面我们讨论了动态规划的一些理论,本节我们将通过几个例子中,动态规划的设计与实现,来了解动态规划的一些特点。
§2.1动态规划的多样性
[例2] 花店橱窗布置问题(IOI99)试题见附录
本题虽然是本届IOI中较为简单的一题,但其中大有文章可作。说它简单,是因为它有序,因此我们一眼便可看出这题应该用动态规划来解决。但是,如何动态规划呢?如何划分阶段,又如何选择状态呢?
<方法1>以花束的数目来划分阶段。在这里,阶段变量k表示的就是要布置的花束数目(前k束花),状态变量sk表示第k束花所在的花瓶。而对于每一个状态sk,决策就是第k-1束花应该放在哪个花瓶,用uk表示。最优指标函数fk(sk)表示前k束花,其中第k束插在第sk个花瓶中,所能取得的最大美学值。
状态转移方程为
规划方程为
(其中A(i,j)是花束i插在花瓶j中的美学值)
边界条件 (V是花瓶总数,事实上这是一个虚拟的边界)
<方法2>以花瓶的数目来划分阶段。在这里阶段变量k表示的是要占用的花瓶数目(前k个花瓶),状态变量sk表示前k个花瓶中放了多少花。而对于任意一个状态sk,决策就是第sk束花是否放在第k个花瓶中,用变量uk=1或0来表示。最优指标函数fk(sk)表示前k个花瓶中插了sk束花,所能取得的最大美学值。
状态转移方程为
规划方程为
边界条件为
两种划分阶段的方法,引出了两种状态表示法,两种规划方式,但是却都成功地解决了问题。只不过因为决策的选择有多有少,所以算法的时间复杂度也就不同。[2]
这个例子具有很大的普遍性。有很多的多阶段决策问题都有着不止一种的阶段划分方法,因而往往就有不止一种的规划方法。有时各种方法所产生的效果是差不多的,但更多的时候,就像我们的例子一样,两种方法会在某个方面有些区别。
所以,在用动态规划解题的时候,可以多想一想是否有其它的解法。对于不同的解法,要注意比较,好的算法好在哪里,差一点的算法差在哪里。从各种不同算法的比较中,我们可以更深刻地领会动态规划的构思技巧。
§2.2动态规划的模式性
这个可能做过动态规划的人都有体会,从我们上面对动态规划的分析也可以看出来。动态规划的设计都有着一定的模式,一般要经历以下几个步骤。
划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。注意这若干个阶段一定要是有序的或者是可排序的,否则问题就无法求解。
选择状态:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。
确定决策并写出状态转移方程:之所以把这两步放在一起,是因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以,如果我们确定了决策,状态转移方程也就写出来了。但事实上,我们常常是反过来做,根据相邻两段的各状态之间的关系来确定决策。
写出规划方程(包括边界条件):在第一部分中,我们已经给出了规划方程的通用形式化表达式。一般说来,只要阶段、状态、决策和状态转移确定了,这一步还是比较简单的。
动态规划的主要难点在于理论上的设计,一旦设计完成,实现部分就会非常简单。大体上的框架如下:
对f1(s1)初始化(边界条件)
for k?2 to n(这里以顺序求解为例)
对每一个sk?Sk
fk(sk)?一个极值(∞或-∞)
对每一个uk(sk)?Dk(sk)
sk-1?Tk(sk,uk)
t?g(fk-1(sk-1),uk)
y t比fk(sk)更优 n
fk(sk)?t
输出fn(sn)
这个N-S图虽然不能代表全部,但足可以概括大多数。少数的一些特殊的动态规划,其实现的原理也是类似,可以类比出来。我们到现在对动态规划的分析,主要是在理论上、设计上,原因也就在此。
掌握了动态规划的模式性,我们在用动态规划解题时就可以把主要的精力放在理论上的设计。一旦设计成熟,问题也就基本上解决了。而且在设计算法时也可以按部就班地来。
但是“物极必反”,太过拘泥于模式就会限制我们的思维,扼杀优良算法思想的产生。我们在解题时,不妨发挥一下创造性,去突破动态规划的实现模式,这样往往会收到意想不到的效果。[3]
§2.3动态规划的技巧性
上面我们所说的动态规划的模式性,主要指的是实现方面。而在设计方面,虽然它较为严格的步骤性,但是它的设计思想却是没有一定的规律可循的。这就需要我们不断地在实践当中去掌握动态规划的技巧,下面仅就一个例子谈一点我自己的体会。
[例3] 街道问题:在下图中找出从左下角到右上角的最短路径,每步只能向右方或上方走。
这是一道简单而又典型的动态规划题,许多介绍动态规划的书与文章中都拿它来做例子。通常,书上的解答是这样的:
按照图中的虚线来划分阶段,即阶段变量k表示走过的步数,而状态变量sk表示当前处于这一阶段上的哪一点(各点所对应的阶段和状态已经用ks在地图上标明)。这时的模型实际上已经转化成了一个特殊的多段图。用决策变量uk=0表示向右走,uk=1表示向上走,则状态转移方程如下:
(这里的row是地图竖直方向的行数)
我们看到,这个状态转移方程需要根据k的取值分两种情况讨论,显得非常麻烦。相应的,把它代入规划方程而付诸实现时,算法也很繁。因而我们在实现时,一般是不会这么做的,而代之以下面方法:
将地图中的点规则地编号如上,得到的规划方程如下:
(这里Distance表示相邻两点间的边长)
这样做确实要比上面的方法简单多了,但是它已经破坏了动态规划的本来面目,而不存在明确的阶段特征了。如果说这种方法是以地图中的行(A、B、C、D)来划分阶段的话,那么它的“状态转移”就不全是在两个阶段之间进行的了。
也许这没什么大不了的,因为实践比理论更有说服力。但是,如果我们把题目扩展一下:在地图中找出从左下角到右上角的两条路径,两条路径中的任何一条边都不能重叠,并且要求两条路径的总长度最短。这时,再用这种“简单”的方法就不太好办了。
如果非得套用这种方法的话,则最优指标函数就需要有四维的下标,并且难以处理两条路径“不能重叠”的问题。
而我们回到原先“标准”的动态规划法,就会发现这个问题很好解决,只需要加一维状态变量就成了。即用sk=(ak,bk)分别表示两条路径走到阶段k时所处的位置,相应的,决策变量也增加一维,用uk=(xk,yk)分别表示两条路径的行走方向。状态转移时将两条路径分别考虑:
在写规划方程时,只要对两条路径走到同一个点的情况稍微处理一下,减少可选的决策个数:
从这个例子中可以总结出设计动态规划算法的一个技巧:状态转移一般是在相邻的两个阶段之间(有时也可以在不相邻的两个阶段间),但是尽量不要在同一个阶段内进行。
动态规划是一种很灵活的解题方法,在动态规划算法的设计中,类似的技巧还有很多。要掌握动态规划的技巧,有两条途径:一是要深刻理解动态规划的本质,这也是我们为什么一开始就探讨它的本质的原因;二是要多实践,不但要多解题,还要学会从解题中探寻规律,总结技巧。
§3动态规划与一些算法的比较
动态规划作为诸多解题方法中的一种,必然和其他一些算法有着诸多联系。从这些联系中,我们也可以看出动态规划的一些特点。
§3.1动态规划与递推
——动态规划是最优化算法
由于动态规划的“名气”如此之大,以至于很多人甚至一些资料书上都往往把一种与动态规划十分相似的算法,当作是动态规划。这种算法就是递推。实际上,这两种算法还是很容易区分的。
按解题的目标来分,信息学试题主要分四类:判定性问题、构造性问题、计数问题和最优化问题。我们在竞赛中碰到的大多是最优化问题,而动态规划正是解决最优化问题的有力武器,因此动态规划在竞赛中的地位日益提高。而递推法在处理判定性问题和计数问题方面也是一把利器。下面分别就两个例子,谈一下递推法和动态规划在这两个方面的联系。
[例4] mod 4 最优路径问题:在下图中找出从第1点到第4点的一条路径,要求路径长度mod 4的余数最小。
这个图是一个多段图,而且是一个特殊的多段图。虽然这个图的形式比一般的多段图要简单,但是这个最优路径问题却不能用动态规划来做。因为一条从第1点到第4点的最优路径,在它走到第2点、第3点时,路径长度mod 4的余数不一定是最小,也就是说最优策略的子策略不一定最优——这个问题不满足最优化原理。
但是我们可以把它转换成判定性问题,用递推法来解决。判断从第1点到第k点的长度mod 4为sk的路径是否存在,用fk(sk)来表示,则递推公式如下:
(边界条件)
(这里lenk,i表示从第k-1点到第k点之间的第i条边的长度,方括号表示“或(or)”运算)
最后的结果就是可以使f4(s4)值为真的最小的s4值。
这个递推法的递推公式和动态规划的规划方程非常相似,我们在这里借用了动态规划的符号也就是为了更清楚地显示这一点。其实它们的思想也是非常相像的,可以说是递推法借用了动态规划的思想解决了动态规划不能解决的问题。
有的多阶段决策问题(像这一题的阶段特征就很明显),由于不能满足最优化原理等使用动态规划的先决条件,而无法应用动态规划。在这时可以将最优指标函数的值当作“状态”放到下标中去,从而变最优化问题为判定性问题,再借用动态规划的思想,用递推法来解决问题。
§3.2动态规划与搜索
——动态规划是高效率、高消费算法
同样是解决最优化问题,有的题目我们采用动态规划,而有的题目我们则需要用搜索。这其中有没有什么规则呢?
我们知道,撇开时空效率的因素不谈,在解决最优化问题的算法中,搜索可以说是“万能”的。所以动态规划可以解决的问题,搜索也一定可以解决。
把一个动态规划算法改写成搜索是非常方便的,状态转移方程、规划方程以及边界条件都可以直接“移植”,所不同的只是求解顺序。动态规划是自底向上的递推求解,而搜索则是自顶向下的递归求解(这里指深度搜索,宽度搜索类似)。
反过来,我们也可以把搜索算法改写成动态规划。状态空间搜索实际上是对隐式图中的点进行枚举,这种枚举是自顶向下的。如果把枚举的顺序反过来,变成自底向上,那么就成了动态规划。(当然这里有个条件,即隐式图中的点是可排序的,详见下一节。)
正因为动态规划和搜索有着求解顺序上的不同,这也造成了它们时间效率上的差别。在搜索中,往往会出现下面的情况:
对于上图(a)这样几个状态构成的一个隐式图,用搜索算法就会出现重复,如上图(b)所示,状态C2被搜索了两次。在深度搜索中,这样的重复会引起以C2为根整个的整个子搜索树的重复搜索;在宽度搜索中,虽然这样的重复可以立即被排除,但是其时间代价也是不小的。而动态规划就没有这个问题,如上图(c)所示。
一般说来,动态规划算法在时间效率上的优势是搜索无法比拟的。(当然对于某些题目,根本不会出现状态的重复,这样搜索和动态规划的速度就没有差别了。)而从理论上讲,任何拓扑有序(现实中这个条件常常可以满足)的隐式图中的搜索算法都可以改写成动态规划。但事实上,在很多情况下我们仍然不得不采用搜索算法。那么,动态规划算法在实现上还有什么障碍吗?
考虑上图(a)所示的隐式图,其中存在两个从初始状态无法达到的状态。在搜索算法中,这样的两个状态就不被考虑了,如上图(b)所示。但是动态规划由于是自底向上求解,所以就无法估计到这一点,因而遍历了全部的状态,如上图(c)所示。
一般说来,动态规划总要遍历所有的状态,而搜索可以排除一些无效状态。更重要的事搜索还可以剪枝,可能剪去大量不必要的状态,因此在空间开销上往往比动态规划要低很多。
如何协调好动态规划的高效率与高消费之间的矛盾呢?有一种折衷的办法就是记忆化算法。记忆化算法在求解的时候还是按着自顶向下的顺序,但是每求解一个状态,就将它的解保存下来,以后再次遇到这个状态的时候,就不必重新求解了。这种方法综合了搜索和动态规划两方面的优点,因而还是很有实用价值的。
§3.3动态规划与网络流
——动态规划是易设计易实现算法
由于图的关系复杂而无序,一般难以呈现阶段特征(除了特殊的图如多段图,或特殊的分段方法如Floyd),因此动态规划在图论中的应用不多。但有一类图,它的点却是有序的,这就是有向无环图。
在有向无环图中,我们可以对点进行拓扑排序,使其体现出有序的特征,从而据此划分阶段。在有向无还图中求最短路径的算法[4],已经体现出了简单的动态规划思想。但动态规划在图论中还有更有价值的应用。下面先看一个例子。
[例6] N个人的街道问题:在街道问题(参见例3)中,若有N个人要从左下角走向右上角,要求他们走过的边的总长度最大。当然,这里每个人也只能向右或向上走。下面是一个样例,左图是从出发地到目的地的三条路径,右图是他们所走过的边,这些边的总长度为5 + 4 + 3 + 6 + 3 + 3 + 5 + 8 + 8 + 7 + 4 + 5 + 9 + 5 + 3 = 78(不一定是最大)。
这个题目是对街道问题的又一次扩展。仿照街道问题的解题方法,我们仍然可以用动态规划来解决本题。不过这一次是N个人同时走,状态变量也就需要用N维来表示,。相应的,决策变量也要变成N维,uk=(uk,1,uk,2,…,uk,N)。状态转移方程不需要做什么改动:
在写规划方程时,需要注意在第k阶段,N条路径所走过的边的总长度的计算,在这里我就用gk(sk,uk)来表示了:
边界条件为
可见将原来的动态规划算法移植到这个问题上来,在理论上还是完全可行的。但是,现在的这个动态规划算法的时空复杂度已经是关于N的指数函数,只要N稍微大一点,这个算法就不可能实现了。
下面我们换一个思路,将N条路径看成是网络中一个流量为N的流,这样求解的目标就是使这个流的费用最大。但是本题又不同于一般的费用流问题,在每一条边e上的流费用并不是流量和边权的乘积 ,而是用下式计算:
为了使经典的费用流算法适用于本题,我们需要将模型稍微转化一下:
如图,将每条边拆成两条。拆开后一条边上有权,但是容量限制为1;另一条边没有容量限制,但是流过这条边就不能计算费用了。这样我们就把问题转化成了一个标准的最大费用固定流问题。
这个算法可以套用经典的最小费用最大流算法,在此就不细说了。(参见附录中的源程序)
这个例题是我仿照IOI97的“障碍物探测器”一题[6]编出来的。“障碍物探测器”比这一题要复杂一些,但是基本思想是相似的。类似的题目还有99年冬令营的“迷宫改造”[7]。从这些题目中都可以看到动态规划和网络流的联系。
推广到一般情况,任何有向无环图中的费用流问题在理论上说,都可以用动态规划来解决。对于流量为N(如果流量不固定,这个N需要事先求出来)的费用流问题,用N维的变量sk=(sk,1,sk,2,…,sk,N)来描述状态,其中sk,i?V(1£i£N)。相应的,决策也用N维的变量uk=(uk,1,uk,2,…,uk,N)来表示,其中uk,i?E(sk,i)(1£i£N),E(v)表示指向v的弧集。则状态转移方程可以这样表示:
sk-1,i = uk,i的弧尾结点
规划方程为
边界条件为
但是,由于动态规划算法是指数级算法,因而在实现中的局限性很大,仅可用于一些N非常小的题目。然而在竞赛解题中,比如上面说到的IOI97以及99冬令营测试时,我们使用动态规划的倾向性很明显(“障碍物探测器”中,我们用的是贪心策略,求N=1或N=2时的局部最优解[8])。这主要有两个原因:
一. 虽然网络流有着经典的算法,但是在竞赛中不可能出现经典的问题。如果要运用网络流算法,则需要经过一番模型转化,有时这个转化还是相当困难的。因此在算法的设计上,灵活巧妙的动态规划算法反而要更为简单一些。
二. 网络流算法实现起来很繁,这是被人们公认的。因而在竞赛的紧张环境中,实现起来有一定模式的动态规划算法又多了一层优势。
正由于动态规划算法在设计和实现上的简便性,所以在N不太大时,也就是在动态规划可行的情况下,我们还是应该尽量运用动态规划。
§4结语
本文的内容比较杂,是我几年来对动态规划的参悟理解、心得体会。虽然主要的篇幅讲的都是理论,但是根本的目的还是指导实践。
动态规划,据我认为,是当今信息学竞赛中最灵活、也最能体现解题者水平的一类解题方法。本文内容虽多,不能涵盖动态规划之万一。“纸上得来终觉浅,绝知此事要躬行。”要想真正领悟、理解动态规划的思想,掌握动态规划的解题技巧,还需要在实践中不断地挖掘、探索。实践得多了,也就能体会到渐入佳境之妙了。
动态规划,
算法之常,
运用之妙,
存乎一心。
❹ 目标跟踪检测算法(四)——多目标扩展
姓名:刘帆;学号:20021210609;学院:电子工程学院
https://blog.csdn.net/qq_34919792/article/details/89893665
【嵌牛导读】基于深度学习的算法在图像和视频识别任务中取得了广泛的应用和突破性的进展。从图像分类问题到行人重识别问题,深度学习方法相比传统方法表现出极大的优势。与行人重识别问题紧密相关的是行人的多目标跟踪问题。
【嵌牛鼻子】深度多目标跟踪算法
【嵌牛提问】深度多目标跟踪算法有哪些?
【嵌牛正文】
第一阶段(概率统计最大化的追踪)
1)多假设多目标追踪算法(MHT,基于kalman在多目标上的拓展)
多假设跟踪算法(MHT)是非常经典的多目标跟踪算法,由Reid在对雷达信号的自动跟踪研究中提出,本质上是基于Kalman滤波跟踪算法在多目标跟踪问题中的扩展。
卡尔曼滤波实际上是一种贝叶斯推理的应用,通过历史关联的预测量和k时刻的预测量来计算后验概率:
关联假设的后验分布是历史累计概率密度的连乘,转化为对数形式,可以看出总体后验概率的对数是每一步观察似然和关联假设似然的求和。但是若同时出现多个轨迹的时候,则需要考虑可能存在的多个假设关联。
左图为k-3时刻三个检测观察和两条轨迹的可能匹配。对于这种匹配关系,可以继续向前预测两帧,如图右。得到一种三层的假设树结构,对于假设树根枝干的剪枝,得到k-3时刻的最终关联结果。随着可能性增加,假设组合会爆炸性增多,为此,只为了保留最大关联性,我们需要对其他的节点进行裁剪。下式为选择方程
实际上MHT不会单独使用,一般作为单目标追踪的扩展添加。
2)基于检测可信度的粒子滤波算法
这个算法分为两个步骤:
1、对每一帧的检测结果,利用贪心匹配算法与已有的对象轨迹进行关联。
其中tr表示一个轨迹,d是某一个检测,他们的匹配亲和度计算包含三个部分:在线更新的分类学习模型(d),用来判断检测结果是不是属于轨迹tr; 轨迹的每个粒子与检测的匹配度,采用中心距离的高斯密度函数求和(d-p)表示;与检测尺寸大小相关的阈值函数g(tr,d),表示检测与轨迹尺度的符合程度, 而α是预设的一个超参数。
计算出匹配亲和度矩阵之后,可以采用二部图匹配的Hungarian算法计算匹配结果。不过作者采用了近似的贪心匹配算法,即首先找到亲和度最大的那个匹配,然后删除这个亲和度,寻找下一个匹配,依次类推。贪心匹配算法复杂度是线性,大部分情况下,也能得到最优匹配结果。
2、利用关联结果,计算每个对象的粒子群权重,作为粒子滤波框架中的观察似然概率。
其中tr表示需要跟踪的对象轨迹,p是某个粒子。指示函数I(tr)表示第一步关联中,轨迹tr是不是关联到某个检测结果,当存在关联时,计算与关联的检测d 的高斯密度P{n}(p-d );C{tr}§是对这个粒子的分类概率;§是粒子通过检测算法得到的检测可信度,(tr)是一个加权函数,计算如下:
3)基于马尔科夫决策的多目标跟踪算法
作者把目标跟踪看作为状态转移的过程,转移的过程用马尔科夫决策过程(MDP)建模。一个马尔科夫决策过程包括下面四个元素:(S, A, T(.),R(.))。其中S表示状态集合,A表示动作集合,T表示状态转移集合,R表示奖励函数集合。一个决策是指根据状态s确定动作a, 即 π: SA。一个对象的跟踪过程包括如下决策过程:
从Active状态转移到Tracked或者Inactive状态:即判断新出现的对象是否是真。
从Tracked状态转移到Tracked或者Lost状态:即判断对象是否是持续跟踪或者暂时处于丢失状态。
从Lost状态转移到Lost或者Tracked或者Inactive状态:即判断丢失对象是否重新被跟踪,被终止,或者继续处于丢失状态。
作者设计了三个奖励函数来描述上述决策过程:
第一个是:
即判断新出现的对象是否为真,y(a)=1时表示转移到跟踪状态,反之转移到终止状态。这是一个二分类问题,采用2类SVM模型学习得到。这里用了5维特征向量:包括x-y坐标、宽、高和检测的分数。
第二个是:
这个函数用来判断跟踪对象下一时刻状态是否是出于继续跟踪,还是处于丢失,即跟踪失败。这里作者用了5个历史模板,每个模板和当前图像块做光流匹配,emedFB表示光流中心偏差, 表示平均重合率。 和 是阈值。
第三个是:
这个函数用来判断丢失对象是否重新跟踪,或者终止,或者保持丢失状态不变。这里当丢失状态连续保持超过 (=50)时,则转向终止,其他情况下通过计算M个检测匹配,来判断是否存在最优的匹配使上式(3-14)奖励最大,并大于0。这里涉及两个问题如何设计特征以及如何学习参数。这里作者构造了12维与模板匹配相关的统计值。而参数的学习采用强化学习过程,主要思想是在犯错时候更新二类分类器值。
第二阶段 深度学习应用
1)基于对称网络的多目标跟踪算法
关于Siamese网络在单目标跟踪深度学习中有了介绍,在这里不再介绍,可以向前参考。
2)基于最小多割图模型的多目标跟踪算法
上述算法中为了匹配两个检测采用LUV图像格式以及光流图像。Tang等人在文献中发现采用深度学习计算的类光流特征(DeepMatching),结合表示能力更强的模型也可以得到效果很好的多目标跟踪结果。
基于DeepMatching特征,可以构造下列5维特征:
其中MI,MU表示检测矩形框中匹配的点的交集大小以及并集大小,ξv和ξw表示检测信任度。利用这5维特征可以学习一个逻辑回归分类器。
同样,为了计算边的匹配代价,需要设计匹配特征。这里,作者采用结合姿态对齐的叠加Siamese网络计算匹配相似度,如图9,采用的网络模型StackNetPose具有最好的重识别性能。
综合StackNetPose网络匹配信任度、深度光流特征(deepMatching)和时空相关度,作者设计了新的匹配特征向量。类似于[2], 计算逻辑回归匹配概率。最终的跟踪结果取得了非常突出的进步。在MOT2016测试数据上的结果如下表:
3)通过时空域关注模型学习多目标跟踪算法
除了采用解决目标重识别问题的深度网络架构学习检测匹配特征,还可以根据多目标跟踪场景的特点,设计合适的深度网络模型来学习检测匹配特征。Chu等人对行人多目标跟踪问题中跟踪算法发生漂移进行统计分析,发现不同行人发生交互时,互相遮挡是跟踪算法产生漂移的重要原因[4]。如图10。
在这里插入图片描述
针对这个问题,文献[4]提出了基于空间时间关注模型(STAM)用于学习遮挡情况,并判别可能出现的干扰目标。如图11,空间关注模型用于生成遮挡发生时的特征权重,当候选检测特征加权之后,通过分类器进行选择得到估计的目标跟踪结果,时间关注模型加权历史样本和当前样本,从而得到加权的损失函数,用于在线更新目标模型。
该过程分三步,第一步是学习特征可见图:
第二步是根据特征可见图,计算空间关注图(Spatial Attention):
其中fatt是一个局部连接的卷积和打分操作。wtji是学习到的参数。
第三步根据空间注意图加权原特征图:
对生成的加权特征图进行卷积和全连接网络操作,生成二元分类器判别是否是目标自身。最后用得到分类打分选择最优的跟踪结果。
4)基于循环网络判别融合表观运动交互的多目标跟踪算法
上面介绍的算法采用的深度网络模型都是基于卷积网络结构,由于目标跟踪是通过历史轨迹信息来判断新的目标状态,因此,设计能够记忆历史信息并根据历史信息来学习匹配相似性度量的网络结构来增强多目标跟踪的性能也是比较可行的算法框架。
考虑从三个方面特征计算轨迹历史信息与检测的匹配:表观特征,运动特征,以及交互模式特征。这三个方面的特征融合以分层方式计算。
在底层的特征匹配计算中,三个特征都采用了长短期记忆模型(LSTM)。对于表观特征,首先采用VGG-16卷积网络生成500维的特征ϕtA,以这个特征作为LSTM的输入计算循环。
对于运动特征,取相对位移vit为基本输入特征,直接输入LSTM模型计算没时刻的输出ϕi,对于下一时刻的检测同样计算相对位移vjt+1,通过全连接网络计算特征ϕj,类似于表观特征计算500维特征ϕm,并利用二元匹配分类器进行网络的预训练。
对于交互特征,取以目标中心位置周围矩形领域内其他目标所占的相对位置映射图作为LSTM模型的输入特征,计算输出特征ϕi,对于t+1时刻的检测计算类似的相对位置映射图为特征,通过全连接网络计算特征ϕj,类似于运动模型,通过全连接网络计算500维特征ϕI,进行同样的分类训练。
当三个特征ϕA,ϕM,ϕI都计算之后拼接为完整的特征,输入到上层的LSTM网络,对输出的向量进行全连接计算,然后用于匹配分类,匹配正确为1,否则为0。对于最后的网络结构,还需要进行微调,以优化整体网络性能。最后的分类打分看作为相似度用于检测与轨迹目标的匹配计算。最终的跟踪框架采用在线的检测与轨迹匹配方法进行计算。
5)基于双线性长短期循环网络模型的多目标跟踪算法
在对LSTM中各个门函数的设计进行分析之后,Kim等人认为仅仅用基本的LSTM模型对于表观特征并不是最佳的方案,在文献[10]中,Kim等人设计了基于双线性LSTM的表观特征学习网络模型。
除了利用传统的LSTM进行匹配学习,或者类似[5]中的算法,拼接LSTM输出与输入特征,作者设计了基于乘法的双线性LSTM模型,利用LSTM的隐含层特征(记忆)信息与输入的乘积作为特征,进行匹配分类器的学习。
这里对于隐含层特征ht-1,必须先进行重新排列(reshape)操作,然后才能乘以输入的特征向量xt。
其中f表示非线性激活函数,mt是新的特征输入。而原始的检测图像采用ResNet50提取2048维的特征,并通过全连接降为256维。下表中对于不同网络结构、网络特征维度、以及不同LSTM历史长度时,表观特征的学习对跟踪性能的影响做了验证。
可以看出采用双线性LSTM(bilinear LSTM)的表观特征性能最好,此时的历史相关长度最佳为40,这个值远远超过文献[5]中的2-4帧历史长度。相对来说40帧历史信息影响更接近人类的直觉。
❺ 菜鸟裹裹怎么预约无人车
菜鸟裹裹无人车可以在“菜鸟”app上预约。无人车到达前会有短信和电话提醒,无人车会在指定位置等待3分钟,在车尾输入取件码即可。
包裹到小区后,用户使用手机淘宝等,可以随时预约方便的上门时间。据悉,每台无人车可存放 18 件快递,在小区行驶一周约1小时,充电一次可行驶40多公里。
技术原理:
菜鸟物流全链路“时空优化”算法中心,专注于研究路径规划、实时调度及数字地图在物流场景中的全链路解决方案。
该算法目前应用于仓储配送、快递、菜鸟裹裹寄件、菜鸟驿站收件等物流场景。例如菜鸟仓储的智能波次生产与手持智能设备打通,指导一线人员日常工作。在城市配送方面,天猫小店、零售通、B2B物流等多场景中,城配路径规划算法指导日常货运司机工作。
在“最后一公里”的体验上,该算法面向天猫超市消费者提供配送员的配送轨迹查询及2小时到达预测服务,为菜鸟裹裹2小时上门取件提供时效预测和智能派单,为菜鸟驿站指导选址。
❻ NP问题真的很难理解
原文地址: http://m.blog.csdn.net/csshuke/article/details/74909562
希望通过这篇文章可以不仅让计算机相关专业的人可以看懂和区分什么是P类问题什么是NP类问题,更希望达到的效果是非专业人士比如学文科的朋友也可以有一定程度的理解。
有一则程序员界的笑话,就是有一哥们去google面试的时候被问到一个问题是:在什么情况下P=NP,然后他的回答是”当N=1的时候”。这是我第一次听说P=NP问题,大概是在临近毕业为找工作而准备的时候。
这几天科技类新闻的头条都被阿尔法狗大战李世石刷爆了,虽然我也不是AI专家,但是也想从普通人的角度来写点东西来聊聊这个有意思的事情,在搜集资料的时候又一次看到了NP问题,于是想开个小差,在写下一篇文章《AI是怎么下围棋的?》之前,先说说这个NP问题哈。
最简单的定义是这样的:
P问题:
一个问题可以在多项式( )的时间复杂度内解决。
NP问题:
一个问题的解可以在多项式的时间内被验证。
NP-hard问题:
任意np问题都可以在多项式时间内归约为该问题,但该问题本身不一定是NP问题。归约的意思是为了解决问题A,先将问题A归约为另一个问题B,解决问题B同时也间接解决了问题A。
NPC问题:
既是NP问题,也是NP-hard问题。
这样的定义虽然简单,但是对于第一次接触P、NP的人来说,就像前一阵问你什么是“引力波”而你回答:引力波是时空的涟漪。从答案中几乎没有得到任何有意义的理解。所以接来来的内容希望不仅计算机相关专业的人可以看懂,希望达到的效果是文科生们也可以有一定程度的理解。
现阶段虽然电脑已经非常的普及,有人用它来上网,有用它来的游戏,有用它来看片,但是很少人有还在乎电脑的本质是计算机,它在给人们的日常生活带来娱乐和方便的同时表现的其实是其庞大的计算能力。日常生活中我们使用的各种五花八门的软件,其实都是一组计算机程序,而程序则可以看作是一系列算法,而我们看到的计算机的硬件的作用就是处理这些算法。这里的所说算法不只是简单的加减乘除,而是包括下面这些要素:
算术运算:加减乘除等运算
逻辑运算:或、且、非等运算
关系运算:大于、小于、等于、不等于等运算
数据传输:输入、输出、赋值等运算
以及通过控制结构来控制处理这些运算或操作的顺序。说到这里有点担心有些朋友已经不是很明白了,举个例子吧。
我们如何从n个数里面挑出最大的数。这个简单吧,就只需要一个一个数的对比过去就行了。具体说也就是先比较n和n-1,记下比较大的那个数,接着我们再比较记下的这个数和n-2,又记下比较大的数,这样一直比到最后一个数。这整个比较的过程我们就可以把其叫作算法,而这个算法就包含了上述的这些要素。给我们的n个数就是算法的输入数据,我们要挑选出最大的那个数就是算法的输出数据,当中我们判断大小的时候必然采用了一些基础的算术运算或关系运算。
希望说到这里大家能够基本理解什么是算法,因为接下来我要花一点时间说说什么是算法的时间复杂度。要计算或解决一个问题,该问题通常有一个大小规模,用n表示。我们还是引用上面的例子,从n个数里面找出最大的那个数,这个n就是该问题的规模大小。怎么找呢?我们要通过比较n-1次才能得到结果,这个n-1次就可以理解为所花的时间,也就是时间复杂度。再比如,将这n个数按从大至小排序,n是其规模大小,若是我们按照这样的方法:第一次从n个数里找最大,第二次从n-1个数里找最大,以此类推,需要的比较次数就是n(n-1)/2。我们所用的方法称之库为算法,那么n(n-1)/2就是该算法的时间复杂度。对于时间复杂度,当n足够大时,我们只注重最高次方的那一项,其他各项可以忽略,另外,其常数系数也不重要,所以,n(n-1)/2我们只重视n的平方这一项了,记为 ,这就是该算法对该问题的时间复杂度的专业表示。
时间复杂度其实并不是表示一个程序解决问题具体需要花多少时间,而是当问题规模扩大后,程序需要的时间长度增长得有多快。也就是说,对于高速处理数据的计算机来说,处理某一个特定数据的效率不能衡量一个程序的好坏,而应该看当这个数据的规模变大到数百倍后,程序运行时间是否还是一样,或者也跟着慢了数百倍,或者变慢了数万倍。不管数据有多大,程序处理花的时间始终是那么多的,我们就说这个程序很好,具有 的时间复杂度,也称常数级复杂度;数据规模变得有多大,花的时间也跟着变得有多长,这个程序的时间复杂度就是 ,比如找n个数中的最大值;而像冒泡排序、插入排序等,数据扩大2倍,时间变慢4倍的,属于 的复杂度。还有一些穷举类的算法,所需时间长度成几何阶数上涨,这就是 的指数级复杂度,甚至 的阶乘级复杂度。不会存在 的复杂度,因为前面的那个“2”是系数,根本不会影响到整个程序的时间增长。同样地, 的复杂度也就是 的复杂度。因此,我们会说,一个 的程序的效率比 的效率低,尽管在n很小的时候,前者优于后者,但后者时间随数据规模增长得慢,最终 的复杂度将远远超过 。我们也说, 的复杂度小于 的复杂度。
Ok,写到这里总算要引入正题了,容易看的出,前面的几类时间复杂度可以分为两种级别:一种是 , , 等,我们把它叫做多项式级的复杂度,因为它的规模n出现在底数的位置;另一种是 和 型复杂度,它是非多项式级的,其复杂度往往计算机都不能承受。
是时候引入P、NP问题的概念了: 如果一个问题可以找到一个能在多项式的时间复杂度里解决它的算法,那么这个问题就属于P问题 。而NP问题的理解并不是NotP,NP问题不是非P类问题。 NP问题是指可以在多项式的时间里验证一个解的问题,NP问题的另一个定义是,可以在多项式的时间里猜出一个解的问题。 P类问题相信不用举太多的例子来说明了,上面提到的找最大数,排序等问题都是P类问题,而要更好的理解NP问题需要另外举一个例子。
大整数因式分解问题-比如有人告诉你数9938550可以分解成两个数的乘积,你不知道到底对不对,但是如果告诉你这两个数是1123和8850,那么很容易就可以用最简单的计算器进行验证。
最短路径问题-某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径——最短路径。
如上图,比如告诉你从点0到点5的最短路径是22,要验证的话只需要0->1,加上1->5,13+9=22,时间复杂度是常量 ,假如从上图的六个点扩大到n个点的话,验证过程所需要的算法时间很杂度也都是 。如果没有告诉你最短路径是多要,要用算法来求解的话,我们可以这样来“猜测”它的解:先求一个总路程不超过 100的方案,假设我们可以依靠极好的运气“猜出”一个路线,使得总长度确实不超过100,那么我们只需要每次猜一条路一共猜n次。接下来我们再找总长度不超过 50 的方案,找不到就将阈值提高到75…… 假设最后找到了总长度为 90 的方案,而找不到总长度小于90的方案。我们最终便在多项式时间 内“猜”到了这个旅行商问题的解是一个长度为 90 的路线。
是否有不是NP问题的问题呢?有。就是对于那些验证解都无法在多项式时间复杂度内完成的问题。比如问:一个图中是否不存在Hamilton回路?
从图中的任意一点出发,最终回到起点,路途中经过图中每一个结点当且仅当一次,则成为哈密顿回路。
验证Hamilton回路只需要把给定的路径走一次看是不是只每个结点只经过一次,而验证不存在Hamilton回路则需要把每条路径都走一遍否则不敢说不存在Hamilton回路。
之所以要特别的定义NP问题,就在于我们不会去为那些无法在多项式时间复杂度内验证的问题去在多项式的时间复杂度内求它的解,有点拗口,但是多看几遍应该明白,通俗的讲就是对于一个问题告诉你答案让你去验证都需要很长很长时间,可以相像要用算法去求解的话必定需要更长时间。
那么反过来说,所有的P类问题都是NP问题。也就是说,能多项式地解决一个问题,必然能多项式地验证一个问题的解——既然正解都出来了,验证任意给定的解也只需要比较一下就可以了,大不了再算一次给你看也只需要多项式的时间复杂度。关键是,人们想知道,是否所有的NP问题都是P类问题,也就是说是否所有可以用多项式时间验证的问题,也可以在多项式时间内求解。我们可以用集合的观点来说明。如果把所有P类问题归为一个集合P中,把所有NP问题划进另一个集合NP中,那么,显然有P属于NP。现在,所有对NP问题的研究都集中在一个问题上,即究竟是否有P=NP? 通常所谓的“NP问题”,其实就一句话:证明或推翻P=NP。
说到这里什么是P类问题什么是NP类问题就讲完了。可能有一些人还不是很清楚,再用通俗但不是很严谨的表述来总结一下。
P类问题就是指那些计算机比较容易算出答案的问题。
NP类问题就是指那些已知答案以后计算机可以比较容易地验证答案的问题。
接下来要进入的话题是为什么P=NP难证明,觉得枯燥的看到这里已经很好了,起码能分清楚P和NP问题了吧,接下来的内容将比较烧脑。
我们先来看一副集合示意图,这副图反映的是P=NP或P!=NP时候的两个集合的效果,其中就出现了NP-Hard和NPC两个新的概念。要说明为什么目前为止P是否等于NP还没有结论,不得不先弄清楚NPC和NP-Hard。
在引入NPC之前我们先来学习一个概念-归约。简单地说,一个问题A可以归约为问题B的意思是说,可以用问题B的解法解决问题A,或者说,问题A可以“变成”问题B。举个例子,现在有两个问题:求解一个一元一次方程和求解一个一元二次方程。那么我们说,前者可以归约为后者,因为知道怎么样解一个一元二次方程那么一定能解出一元一次方程,因为一元一次方程是一个二次项系数为零的一元二次方程。“问题A可归约为问题B”,那么很容易理解问题B比问题A难,要解决问题B的时间复杂度也就应该大于或等于解决问题A的时间复杂度。而且归约有一项重要的性质:传递性。如果问题A可归约为问题B,问题B可归约为问题C,则问题A一定可归约为问题C,这应该很容易理解吧。现在再来说一下归约的标准概念: 如果能找到这样一个变化法则,对任意一个程序A的输入,都能按这个法则变换成程序B的输入,使两程序的输出相同,那么我们说,问题A可归约为问题B。
从归约的定义中我们看到,一个问题归约为另一个问题,时间复杂度增加了,问题的应用范围也增大了。通过对某些问题的不断归约,我们能够不断寻找复杂度更高,但应用范围更广的算法来代替复杂度虽然低,但只能用于很小的一类问题的算法。那么如果把一个NP问题不断地归约上去,那么最后是否有可能找到一个时间复杂度最高,并且能“通吃”所有的NP问题的这样一个超级NP问题?答案居然是肯定的。也就是说,存在这样一个NP问题,所有的NP问题都可以归约成它,并且这种问题不只一个,它有很多个,它是一类问题。这一类问题就是传说中的NPC问题,也就是NP-完全问题。所以NPC问题的定义非常简单。同时满足下面两个条件的问题就是NPC问题。 首先,它得是一个NP问题;然后,所有的NP问题都可以归约到它。
既然所有的NP问题都能归约成NPC问题,那么只要任意一个NPC问题找到了一个多项式的算法,那么所有的NP问题都能用这个算法解决了,那么NP也就等于P了。因此,目前NPC问题还没有多项式的有效算法,只能用指数级甚至阶乘级复杂度的算法来解决,那么意思就是如果能够找到一个能用多项式时间复杂度解决的NPC问题就证明了P=NP了。
而说到NP-Hard问题。NP-Hard问题是这样一种问题,它满足NPC问题定义的第二条但不一定要满足第一条,就是说所有的NP问题都能归化到它,但它本身并不一定是个NP问题,也就是即使有一天发现了NPC问题的多项式算法,但NP-Hard问题仍然无法用多项式算法解决,因为它不是NP问题,对于答案的验证都很困难。
下面引用Matrix67文章里的逻辑电路的例子来说明NPC问题。
逻辑电路问题是指的这样一个问题:给定一个逻辑电路,问是否存在一种输入使输出为True。
什么叫做逻辑电路呢?一个逻辑电路由若干个输入,一个输出,若干“逻辑门”和密密麻麻的线组成。看下面一例,不需要解释你马上就明白了。
这是个较简单的逻辑电路,当输入1、输入2、输入3分别为True、True、False或False、True、False时,输出为True。
有输出无论如何都不可能为True的逻辑电路吗?有。下面就是一个简单的例子。
上面这个逻辑电路中,无论输入是什么,输出都是False。我们就说,这个逻辑电路不存在使输出为True的一组输入。
回到上文,给定一个逻辑电路,问是否存在一种输入使输出为True,这即逻辑电路问题。
逻辑电路问题属于NPC问题。这是有严格证明的。它显然属于NP问题,并且可以直接证明所有的NP问题都可以归约到它。证明过程相当复杂,其大概意思是说任意一个NP问题的输入和输出都可以转换成逻辑电路的输入和输出(想想计算机内部也不过是一些0和1的运算),因此对于一个NP问题来说,问题转化为了求出满足结果为True的一个输入(即一个可行解)。
类似这样的NPC问题,目前还没有找到在多项式复杂度内可以求解的算法,所以说一旦这样的问题都变得多项式复杂度内可解的话,很多问题都可以通过现有的计算机技术进行求解。就比如电脑下围棋,验证一局棋的结果显然是很简单的,但要保证每局都能赢的话目前的方法需要电脑穷举出所有的可能性,并根据每一步棋的变化搜索出最终达到胜利的下棋路径,目前的计算机性能显然是达不到的。因为围棋的状态空间复杂度达到了10的172方,而围棋的博弈树复杂度达到了10的300次方,光看数字可能不直观,一句话就是围棋的变化多过宇宙的原子数量!
所以对于围棋这样的游戏人工智能如果要战胜人类需要实现下面两个条件中的任何一个:
计算机性能无限强大,可以穷举所有可能性;
研究出新的算法,在不穷举的情况下也能保证赢;
目前 Google 的 AlphaGo所做的只不过是通过优化算法提高穷举效率,同时利用现有的大数据与云计算来提升计算性能而已 。下面一篇文章将更详细的介绍AI是如何下围棋的,敬请期待。
❼ 衡量算法效率的方法与准则
算法效率与分析
数据结构作为程序设计的基础,其对算法效率的影响必然是不可忽视的。本文就如何合理选择数据结构来优化算法这一问题,对选择数据结构的原则和方法进行了一些探讨。首先对数据逻辑结构的重要性进行了分析,提出了选择逻辑结构的两个基本原则;接着又比较了顺序和链式两种存储结构的优点和缺点,并讨论了选择数据存储结构的方法;最后本文从选择数据结构的的另一角度出发,进一步探讨了如何将多种数据结构进行结合的方法。在讨论方法的同时,本文还结合实际,选用了一些较具有代表性的信息学竞赛试题举例进行了分析
【正文】一、引论
“数据结构+算法=程序”,这就说明程序设计的实质就是对确定的问题选择一种合适的数据结构,加上设计一种好的算法。由此可见,数据结构在程序设计中有着十分重要的地位。
数据结构是相互之间存在一种或多种特定关系的数据元素的集合。因为这其中的“关系”,指的是数据元素之间的逻辑关系,因此数据结构又称为数据的逻辑结构。而相对于逻辑结构这个比较抽象的概念,我们将数据结构在计算机中的表示又称为数据的存储结构。
建立问题的数学模型,进而设计问题的算法,直至编出程序并进行调试通过,这就是我们解决信息学问题的一般步骤。我们要建立问题的数学模型,必须首先找出问题中各对象之间的关系,也就是确定所使用的逻辑结构;同时,设计算法和程序实现的过程,必须确定如何实现对各个对象的操作,而操作的方法是决定于数据所采用的存储结构的。因此,数据逻辑结构和存储结构的好坏,将直接影响到程序的效率。
二、选择合理的逻辑结构
在程序设计中,逻辑结构的选用就是要分析题目中的数据元素之间的关系,并根据这些特定关系来选用合适的逻辑结构以实现对问题的数学描述,进一步解决问题。逻辑结构实际上是用数学的方法来描述问题中所涉及的操作对象及对象之间的关系,将操作对象抽象为数学元素,将对象之间的复杂关系用数学语言描述出来。
根据数据元素之间关系的不同特性,通常有以下四种基本逻辑结构:集合、线性结构、树形结构、图状(网状)结构。这四种结构中,除了集合中的数据元素之间只有“同属于一个集合”的关系外,其它三种结构数据元素之间分别为“一对一”、“一对多”、“多对多”的关系。
因此,在选择逻辑结构之前,我们应首先把题目中的操作对象和对象之间的关系分析清楚,然后再根据这些关系的特点来合理的选用逻辑结构。尤其是在某些复杂的问题中,数据之间的关系相当复杂,且选用不同逻辑结构都可以解决这一问题,但选用不同逻辑结构实现的算法效率大不一样。
对于这一类问题,我们应采用怎样的标准对逻辑结构进行选择呢?
下文将探讨选择合理逻辑结构应充分考虑的两个因素。
一、 充分利用“可直接使用”的信息。
首先,我们这里所讲的“信息”,指的是元素与元素之间的关系。
对于待处理的信息,大致可分为“可直接使用”和“不可直接使用”两类。对于“可直接使用”的信息,我们使用时十分方便,只需直接拿来就可以了。而对于“不可直接使用”的这一类,我们也可以通过某些间接的方式,使之成为可以使用的信息,但其中转化的过程显然是比较浪费时间的。
由此可见,我们所需要的是尽量多的“可直接使用”的信息。这样的信息越多,算法的效率就会越高。
对于不同的逻辑结构,其包含的信息是不同的,算法对信息的利用也会出现不同的复杂程度。因此,要使算法能够充分利用“可直接使用”的信息,而避免算法在信息由“不可直接使用”向“可直接使用”的转化过程中浪费过多的时间,我们必然需要采用一种合理的逻辑结构,使其包含更多“可直接使用”的信息。
〖问题一〗 IOI99的《隐藏的码字》。
〖问题描述〗
问题中给出了一些码字和一个文本,要求编程找出文本中包含这些码字的所有项目,并将找出的项目组成一个最优的“答案”,使得答案中各项目所包含的码字长度总和最大。每一个项目包括一个码字,以及该码字在文本中的一个覆盖序列(如’abcadc’就是码字’abac’的一个覆盖序列),并且覆盖序列的长度不超过1000。同时,“答案”要求其中每个项目的覆盖序列互相没有重叠。
〖问题分析〗
对于此题,一种较容易得出的基本算法是:对覆盖序列在文本中的终止位置进行循环,再判断包含了哪些码字,找出所有项目,并最后使用动态规划的方法将项目组成最优的“答案”。
算法的其它方面我们暂且不做考虑,而先对问题所采用的逻辑结构进行选择。
如果我们采用线性的逻辑结构(如循环队列),那么我们在判断是否包含某个码字t时,所用的方法为:初始时用指针p指向终止位置,接着通过p的不断前移,依次找出码字t从尾到头的各个字母。例如码字为“ABDCAB”,而文本图1-1,终止位置为最右边的箭头符号,每个箭头代表依次找到的码字的各个字母。
指针p的移动方向
A B D C A B
C D A C B D C A D C D B A D C C B A D
图1-1
由于题目规定码字的覆盖序列长度不超过1000,所以进行这样的一次是否包含的判断,其复杂度为O(1000)。
由于码字t中相邻两字母在文本中的位置,并非只有相邻(如图1-1中的’D’和’C’)这一种关系,中间还可能间隔了许多的字母(如图1-1中’C’和’A’就间隔了2个字母),而线性结构中拥有的信息,仅仅只存在于相邻的两元素之间。通过这样简单的信息来寻找码字的某一个字母,其效率显然不高。
如果我们建立一个有向图,其中顶点i(即文本的第i位)用52条弧分别连接’a’..’z’,’A’..’Z’这52个字母在i位以前最后出现的位置(如图1-2的连接方式),我们要寻找码字中某个字母的前一个字母,就可以直接利用已连接的边,而不需用枚举的方法。我们也可以把问题看为:从有向图的一个顶点出发,寻找一条长度为length(t)-1的路径,并且路径中经过的顶点,按照码字t中的字母有序。
C D A C B D C A D C D B A D C C B A D
图1-2
通过计算,用图进行记录在空间上完全可以承受(记录1000个点×52条弧×4字节的长整型=200k左右)。在时间上,由于可以充分利用第i位和第i+1位弧的连接方式变化不大这一点(如图1-2所示,第i位和第i+1位只有一条弧的指向发生了变化,即第i+1位将其中一条弧指向了第i位),所以要对图中的弧进行记录,只需对弧的指向进行整体赋值,并改变其中的某一条弧即可。
因此,我们通过采用图的逻辑结构,使得寻找字母的效率大大提高,其判断的复杂度为O(length(t)),最坏为O(100),比原来方法的判断效率提高了10倍。
(附程序codes.pas)
对于这个例子,虽然用线性的数据结构也可以解决,但由于判断的特殊性,每次需要的信息并不能从相邻的元素中找到,而线性结构中只有相邻元素之间存在关系的这一点,就成为了一个很明显的缺点。因此,问题一线性结构中的信息,就属于“不可直接使用”的信息。相对而言,图的结构就正好满足了我们的需要,将所有可能产生关系的点都用弧连接起来,使我们可以利用弧的关系,高效地进行判断寻找的过程。虽然图的结构更加复杂,但却将“不可直接使用”的信息,转化成为了“可直接使用”的信息,算法效率的提高,自然在情理之中。。
二、 不记录“无用”信息。
从问题一中我们看到,由于图结构的信息量大,所以其中的信息基本上都是“可用”的。但是,这并不表示我们就一定要使用图的结构。在某些情况下,图结构中的“可用”信息,是有些多余的。
信息都“可用”自然是好事,但倘若其中“无用”(不需要)的信息太多,就只会增加我们思考分析和处理问题时的复杂程度,反而不利于我们解决问题了。
〖问题二〗 湖南省1997年组队赛的《乘船问题》
〖问题描述〗
有N个人需要乘船,而每船最多只能载两人,且必须同名或同姓。求最少需要多少条船。
〖问题分析〗
看到这道题,很多人都会想到图的数据结构:将N个人看作无向图的N个点,凡同名或同姓的人之间都连上边。
要满足用船最少的条件,就是需要尽量多的两人共乘一条船,表现在图中就是要用最少的边完成对所有顶点的覆盖。这就正好对应了图论的典型问题:求最小边的覆盖。所用的算法为“求任意图最大匹配”的算法。
使用“求任意图最大匹配”的算法比较复杂(要用到扩展交错树,对花的收缩等等),效率也不是很高。因此,我们必须寻找一个更简单高效的方法。
首先,由于图中任两个连通分量都是相对独立的,也就是说任一条匹配边的两顶点,都只属于同一个连通分量。因此,我们可以对每个连通分量分别进行处理,而不会影响最终的结果。
同时,我们还可以对需要船只s的下限进行估计:
对于一个包含Pi个顶点的连通分量,其最小覆盖边数显然为[Pi/2]。若图中共有L个连通分量,则s=∑[Pi/2](1<=i<=L)。
然后,我们通过多次尝试,可得出一个猜想:
实际需要的覆盖边数完全等于我们求出的下限∑[Pi/2](1<=i<=L)。
要用图的结构对上述猜想进行证明,可参照以下两步进行:
1. 连通分量中若不存在度为1的点,就必然存在回路。
2. 从图中删去度为1的点及其相邻的点,或删去回路中的任何一边,连通分量依然连通,即连通分量必然存在非桥边。
由于图的方法不是这里的重点,所以具体证明不做详述。而由采用图的数据结构得出的算法为:每次输出一条非桥的边,并从图中将边的两顶点删去。此算法的时间复杂度为O(n3)。(寻找一条非桥边的复杂度为O(n2),寻找覆盖边操作的复杂度为O(n))
由于受到图结构的限制,时间复杂度已经无法降低,所以如果我们要继续对算法进行优化,只有考虑使用另一种逻辑结构。这里,我想到了使用二叉树的结构,具体说就是将图中的连通分量都转化为二叉树,用二叉树来解决问题。
首先,我们以连通分量中任一个顶点作为树根,然后我们来确定建树的方法。
1. 找出与根结点i同姓的点j(j不在二叉树中)作为i的左儿子,再以j为树根建立子树。
2. 找出与根结点i同名的点k(k不在二叉树中)作为i的右儿子,再以k为树根建立子树。
如图2-1-1中的连通分量,我们通过上面的建树方法,可以使其成为图2-1-2中的二叉树的结构(以结点1为根)。(两点间用实线表示同姓,虚线表示同名)
图2-1-2
图2-1-1
接着,我就来证明这棵树一定包含了连通分量中的所有顶点。
【引理2.1】
若二叉树T中包含了某个结点p,那么连通分量中所有与p同姓的点一定都在T中。
证明:
为了论证的方便,我们约定:s表示与p同姓的顶点集合;lc[p,0]表示结点p,lc[p,i](i>0)表示lc[p,i-1]的左儿子,显然lc[p,i]与p是同姓的。
假设存在某个点q,满足qs且qT。由于s是有限集合,因而必然存在某个lc[p,k]无左儿子。则我们可以令lc[p,k+1]=q,所以qT,与假设qT相矛盾。
所以假设不成立,原命题得证。
由引理2.1的证明方法,我们同理可证引理2.2。
【引理2.2】
若二叉树T中包含了某个结点p,那么连通分量中所有与p同名的点一定都在T中。
有了上面的两个引理,我们就不难得出下面的定理了。
【定理一】
以连通分量中的任一点p作为根结点的二叉树,必然能够包含连通分量中的所有顶点。
证明:
由引理2.1和引理2.2,所有与p同姓或同名的点都一定在二叉树中,即连通分量中所有与p有边相连的点都在二叉树中。由连通分量中任两点间都存在路径的特性,该连通分量中的所有点都在二叉树中。
在证明二叉树中包含了连通分量的所有顶点后,我们接着就需要证明我们的猜想,也就是下面的定理:
【定理二】包含m个结点的二叉树Tm,只需要船的数量为boat[m]=[m/2](mN)。
证明:
(i) 当m=1,m=2,m=3时命题显然成立。
图2-2-1
图2-2-2
图2-2-3
(ii) 假设当m<k(k>3)时命题成立,那么当m=k时,我们首先从树中找到一个层次最深的结点,并假设这个结点的父亲为p。那么,此时有且只有以下三种情况(结点中带有阴影的是p结点):
(1) 如图2-2-1,p只有一个儿子。此时删去p和p唯一的儿子,Tk就成为了Tk-2,则boat[k]=boat[k-2]+1=[(k-2)/2]+1=[k/2]。
(2) 如图2-2-2,p有两个儿子,并且p是其父亲的左儿子。此时可删去p和p的右儿子,并可将p的左儿子放到p的位置上。同样地,Tk成为了Tk-2,boat[k]=boat[k-2]+1=[k/2]。
(3) 如图2-2-3,p有两个儿子,并且p是其父亲的右儿子。此时可删去p和p的左儿子,并可将p的右儿子放到p的位置上。情况与(2)十分相似,易得此时得boat[k]=boat[k-2]+1=[k/2]。
综合(1)、(2)、(3),当m=k时,boat[k]=[k/2]。
最后,综合(i)、(ii),对于一切mN,boat[m]=[m/2]。
由上述证明,我们将问题中数据的图结构转化为树结构后,可以得出求一棵二叉树的乘船方案的算法:
proc try(father:integer;var root:integer;var rest:byte);
{输出root为树根的子树的乘船方案,father=0表示root是其父亲的左儿子,
father=1表示root是其父亲的右儿子,rest表示输出子树的乘船方案后,
是否还剩下一个根结点未乘船}
begin
visit[root]:=true; {标记root已访问}
找到一个与root同姓且未访问的结点j;
if j<>n+1 then try(0,j,lrest);
找到一个与root同姓且未访问的结点k;
if k<>n+1 then try(1,k,rrest);
if (lrest=1) xor (rrest=1) then begin {判断root是否只有一个儿子,情况一}
if lrest=1 then print(lrest,root) else print(rrest,root);
rest:=0;
end
else if (lrest=1) and (rrest=1) then begin {判断root是否有两个儿子}
if father=0 then begin
print(rrest,root);root:=j; {情况二}
end
else begin
print(lrest,root);root:=k; {情况三}
end;
rest:=1;
end
else rest:=1;
end;
这只是输出一棵二叉树的乘船方案的算法,要输出所有人的乘船方案,我们还需再加一层循环,用于寻找各棵二叉树的根结点,但由于每个点都只会访问一次,寻找其左右儿子各需进行一次循环,所以算法的时间复杂度为O(n2)。(附程序boat.pas)
最后,我们对两种结构得出不同时间复杂度算法的原因进行分析。其中最关键的一点就是因为二叉树虽然结构相对较简单,但已经包含了几乎全部都“有用”的信息。由我们寻找乘船方案的算法可知,二叉树中的所有边不仅都发挥了作用,而且没有重复的使用,可见信息的利用率也是相当之高的。
既然采用树结构已经足够,图结构中的一些信息就显然就成为了“无用”的信息。这些多余的“无用”信息,使我们在分析问题时难于发现规律,也很难找到高效的算法进行解决。这正如迷宫中的墙一样,越多越难走。“无用”的信息,只会干扰问题的规律性,使我们更难找出解决问题的方法。
小结
我们对数据的逻辑结构进行选择,是构造数学模型一大关键,而算法又是用来解决数学模型的。要使算法效率高,首先必须选好数据的逻辑结构。上面已经提出了选择逻辑结构的两个条件(思考方向),总之目的是提高信息的利用效果。利用“可直接使用”的信息,由于中间不需其它操作,利用的效率自然很高;不不记录“无用”的信息,就会使我们更加专心地研究分析“有用”的信息,对信息的使用也必然会更加优化。
总之,在解决问题的过程中,选择合理的逻辑结构是相当重要的环
三、 选择合理的存储结构
数据的存储结构,分为顺序存储结构和链式存储结构。顺序存储结构的特点是借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系;链式存储结构则是借助指示元素存储地址的指针表示数据元素之间的逻辑关系。
因为两种存储结构的不同,导致这两种存储结构在具体使用时也分别存在着优点和缺点。
这里有一个较简单的例子:我们需要记录一个n×n的矩阵,矩阵中包含的非0元素为m个。
此时,我们若采用顺序存储结构,就会使用一个n×n的二维数组,将所有数据元素全部记录下来;若采用链式存储结构,则需要使用一个包含m个结点的链表,记录所有非0的m个数据元素。由这样两种不同的记录方式,我们可以通过对数据的不同操作来分析它们的优点和缺点。
1. 随机访问矩阵中任意元素。由于顺序结构在物理位置上是相邻的,所以可以很容易地获得任意元素的存储地址,其复杂度为O(1);对于链式结构,由于不具备物理位置相邻的特点,所以首先必须对整个链表进行一次遍历,寻找需进行访问的元素的存储地址,其复杂度为O(m)。此时使用顺序结构显然效率更高。
2. 对所有数据进行遍历。两种存储结构对于这种操作的复杂度是显而易见的,顺序结构的复杂度为O(n2),链式结构为O(m)。由于在一般情况下m要远小于n2,所以此时链式结构的效率要高上许多。
除上述两种操作外,对于其它的操作,这两种结构都不存在很明显的优点和缺点,如对链表进行删除或插入操作,在顺序结构中可表示为改变相应位置的数据元素。
既然两种存储结构对于不同的操作,其效率存在较大的差异,那么我们在确定存储结构时,必须仔细分析算法中操作的需要,合理地选择一种能够“扬长避短”的存储结构。
一、合理采用顺序存储结构。
我们在平常做题时,大多都是使用顺序存储结构对数据进行存储。究其原因,一方面是出于顺序结构操作方便的考虑,另一方面是在程序实现的过程中,使用顺序结构相对于链式结构更便于对程序进行调试和查找错误。因此,大多数人习惯上认为,能够使用顺序结构进行存储的问题,最“好”采用顺序存储结构。
其实,这个所谓的“好”只是一个相对的标准,是建立在以下两个前提条件之下的:
1. 链式结构存储的结点与顺序结构存储的结点数目相差不大。这种情况下,由于存储的结点数目比较接近,使用链式结构完全不能体现出记录结点少的优点,并且可能会由于指针操作较慢而降低算法的效率。更有甚者,由于指针自身占用的空间较大,且结点数目较多,因而算法对空间的要求可能根本无法得到满足。
2. 并非算法效率的瓶颈所在。由于不是算法最费时间的地方,这里是否进行改进,显然是不会对整个算法构成太大影响的,若使用链式结构反而会显得操作过于繁琐。
二、必要时采用链式存储结构。
上面我对使用顺序存储结构的条件进行了分析,最后就只剩下何时应该采用链式存储结构的问题了。
由于链式结构中指针操作确实较繁琐,并且速度也较慢,调试也不方便,因而大家一般都不太愿意用链式的存储结构。但是,这只是一般的观点,当链式结构确实对算法有很大改进时,我们还是不得不进行考虑的。
〖问题三〗 IOI99的《地下城市》。
〖问题描述〗
已知一个城市的地图,但未给出你的初始位置。你需要通过一系列的移动和探索,以确定初始时所在的位置。题目的限制是:
1. 不能移动到有墙的方格。
2. 只能探索当前所在位置四个方向上的相邻方格。
在这两个限制条件下,要求我们的探索次数(不包括移动)尽可能的少。
〖问题分析〗
由于存储结构要由算法的需要确定,因此我们首先来确定问题的算法。
经过对问题的分析,我们得出解题的基本思想:先假设所有无墙的方格都可能是初始位置,再通过探索一步步地缩小初始位置的范围,最终得到真正的初始位置。同时,为提高算法效率,我们还用到了分治的思想,使我们每一次探索都尽量多的缩小初始位置的范围(使程序尽量减少对运气的依赖)。
接着,我们来确定此题的存储结构。
由于这道题的地图是一个二维的矩阵,所以一般来讲,采用顺序存储结构理所当然。但是,顺序存储结构在这道题中暴露了很大的缺点。我们所进行的最多的操作,一是对初始位置的范围进行筛选,二是判断要选择哪个位置进行探索。而这两种操作,所需要用到的数据,只是庞大地图中很少的一部分。如果采用顺序存储结构(如图3-1中阴影部分表示已标记),无论你需要用到多少数据,始终都要完全的遍历整个地图。
4
3
2
1
1 2 3 4
图3-1
head
图3-2
然而,如果我们采用的是链式存储结构(如图3-2的链表),那么我们需要多少数据,就只会遍历多少数据,这样不仅充分发挥了链式存储结构的优点,而且由于不需单独对某一个数据进行提取,每次都是对所有数据进行判断,从而避免了链式结构的最大缺点。
我们使用链式存储结构,虽然没有降低问题的时间复杂度(链式存储结构在最坏情况下的存储量与顺序存储结构的存储量几乎相同),但由于体现了前文所述选择存储结构时扬长避短的原则,因而算法的效率也大为提高。(程序对不同数据的运行时间见表3-3)
测试数据编号 使用顺序存储结构的程序 使用链式存储结构的程序
1 0.06s 0.02s
2 1.73s 0.07s
3 1.14s 0.06s
4 3.86s 0.14s
5 32.84s 0.21s
6 141.16s 0.23s
7 0.91s 0.12s
8 6.92s 0.29s
9 6.10s 0.23s
10 17.41s 0.20s
表3-3
(附使用链式存储结构的程序under.pas)
我们选择链式的存储结构,虽然操作上可能稍复杂一些,但由于改进了算法的瓶颈,算法的效率自然也今非昔比。由此可见,必要时选择链式结构这一方法,其效果是不容忽视的。
小结
合理选择逻辑结构,由于牵涉建立数学模型的问题,可能大家都会比较注意。但是对存储结构的选择,由于不会对算法复杂度构成影响,所以比较容易忽视。那么,这种不能降低算法复杂度的方法是否需要重视呢?
大家都知道,剪枝作为一种常用的优化算法的方法,被广泛地使用,但剪枝同样是无法改变算法的复杂度的。因此,作用与剪枝相似的存储结构的合理选择,也是同样很值得重视的。
总之,我们在设计算法的过程中,必须充分考虑存储结构所带来的不同影响,选择最合理的存储结构。
四、 多种数据结构相结合
上文所探讨的,都是如何对数据结构进行选择,其中包含了逻辑结构的选择和存储结构的选择,是一种具有较大普遍性的算法优化方法。对于多数的问题,我们都可以通过选择一种合理的逻辑结构和存储结构以达到优化算法的目的。
但是,有些问题却往往不如人愿,要对这类问题的数据结构进行选择,常常会顾此失彼,有时甚至根本就不存在某一种合适的数据结构。此时,我们是无法选择出某一种合适的数据结构的,以上的方法就有些不太适用了。
为解决数据结构难以选择的问题,我们可以采用将多种数据结构进行结合的方法。通过多种数据结构相结合,达到取长补短的作用,使不同的数据结构在算法中发挥出各自的优势。
这只是我们将多种数据结构进行结合的总思想,具体如何进行结合,我们可以先看下面的例子。
我们可以采用映射的方法,将线性结构中的元素与堆中间的结点一一对应起来,若线性的数组中的元素发生变化,堆中相应的结点也接着变化,堆中的结点发生变化,数组中相应的元素也跟着变化。
将两种结构进行结合后,无论是第一步还是第二步,我们都不需对所有元素进行遍历,只需进行常数次复杂度为O(log2n)的堆化操作。这样,整个时间复杂度就成为了O(nlog2n),算法效率无疑得到了很大提高。
五、 总结
我们平常使用数据结构,往往只将其作为建立模型和算法实现的工具,而没有考虑这种工具对程序效率所产生的影响。信息学问题随着难度的不断增大,对算法时空效率的要求也越来越高,而算法的时空效率,在很大程度上都受到了数据结构的制约。
❽ 蓝胖子机器智能:核心算法促物流提效丨强链补链在行动
手指轻轻一动、鼠标轻轻一点,快递就能准时送货上门。不少人都在感叹:“现在的快递真方便!”确实,不管是要求“鲜、快、优”的生鲜产品,还是需要精准温控的各种药物,抑或是“大块头”的家电家具和工业配件,都成了快递运输的主要品种。在电商行业迅速发展的当下,物流行业的自动化与智能化已经成为必然。
核心算法让物流环节更高效
现阶段,物流行业发展迅速,在自动化与智能化方面有很大的优化空间。在嗅到其中的市场机会后,不少公司瞄准了这一赛道,对智能物流领域进行布局。
在将机器人、人工智能技术与实际物流环节相结合的过程中,算法是其中绕不开的关键词。目前,蓝胖子在算法方面的布局有四大方向。
第一个方向是计算机视觉。换句话说,就是让机器人的眼睛能够识别不同物品,尤其是在物品重叠时候的分割。
第二个方向是机械臂的控制和运动规划。针对这点,张羽雪也给出了进一步解释。“比如我已经识别到某个包裹了,我要把它从a点抓到b点。”她说,“这个三维空间里其实是有无数条运动轨迹的,我们要找到最快、最短的这一条路径,这就是运动规划。”
第三个方向是移动和多机协作。移动机器人在给定的区域里自主移动、自主导航、自主避障、自主充电。“如果我有10台、20台、50台甚至100台移动机器人,我应该怎么调动每一台机器人,使它们在运作过程中不会相互碰撞?当一台机器人在运作的时候,如何缩短另一台机器人的等待时间?这就需要多机调度技术。”张羽雪说。
第四个就是人工智能技术的应用。在遇到新场景后,机器人需要通过深度学习进行视觉训练,以快速识别新的场景。装箱的规划也是人工智能技术的应用之一。“这里面(装箱)用到了时空优化技术,就是去优化装箱,让所用的时间最少,所用的空间最大。”张羽雪表示。
技术与物流业务需要紧密结合
机器视觉、深度学习等技术应用需要积累大量的数据,也需要较多的试错过程。这个过程的成本很高,对包括蓝胖子在内的所有人工智能企业都提出了不小的挑战。
在张羽雪看来,应对此类挑战的方法主要有两个,一方面需要算力的提升、海量的数据以及更快的通信,另一方面就是需要技术与业务的紧密结合。
从第一个方面来看,算力的提升与整个行业底层技术的提高息息相关,数据的获取则离不开企业与客户的相互合作。客户提供的数据能够训练和测试算法。张羽雪表示,“客户的数据对算法的积累和机器人的训练会起到很好的作用。”
通信速度的提升对机器人的应用也有着极大助力。当前,5G的使用让机器人计算的速度更快,数据传输更快,延时更低,协作更高效。
事实上,机器人的控制系统与客户的各个数据系统,包括ERP(企业资源计划)系统、仓储管理系统和生产规划系统的连接与打通其实是非常难的一点。在实验室里面,很多技术问题都是可以解决的,但并不是所有的技术都能解决业务问题。
张羽雪还呼吁市场应该多给智能机器人行业一些成长的时间。智能机器人的技术积累需要一定时间,从技术到应用需要不断地进行试错,还要在各种场景中不断地训练机器人,使机器人达到更高的作业精度和准确度。在这点上,政府引导基金和民间资本都需要给科创企业更多的时间和更大力度的支持,传统企业也需要逐步转变思维,将目光放长远,采用新兴技术来解决当前以及未来的挑战。
生态的构建对每个行业的发展都非常关键。张羽雪认为,政府是构建生态的一大主体,通过发挥主导作用,政府能够建立本地的相关企业集群。此外,德勤、凯捷等IT服务及咨询公司是连接新兴企业与传统大企业的桥梁,将大客户的需求与新型企业的技术充分对接,也是构建产业生态的一大力量“如果某个大客户有需求,咨询公司就去寻找哪一个初创企业可以提供给他们相应的技术,为客户设计一个整体解决方案。”张羽雪说,“很多大企业已经开始在搭建这样的AI生态了,这也是未来产业的发展趋势。”
编辑丨赵晨
美编丨马利亚
❾ pascal题目
2003年国家集训队论文,王知昆,浅谈用极大化思想解决最大子矩形问题
网上有的,O(n²)的复杂度,n,m是5000都能1秒出解
浅谈用极大化思想解决最大子矩形问题
福州第三中学 王知昆
【摘要】
本文针对一类近期经常出现的有关最大(或最优)子矩形及相关变形问题,介绍了极大化思想在这类问题中的应用。分析了两个具有一定通用性的算法。并通过一些例题讲述了这些算法选择和使用时的一些技巧。
【关键字】 矩形,障碍点,极大子矩形
【正文】
一、 问题
最大子矩形问题:在一个给定的矩形网格中有一些障碍点,要找出网格内部不包含任何障碍点,且边界与坐标轴平行的最大子矩形。
这是近期经常出现的问题,例如冬令营2002的《奶牛浴场》,就属于最大子矩形问题。
Winter Camp2002,奶牛浴场
题意简述:(原题见论文附件)
John要在矩形牛场中建造一个大型浴场,但是这个大型浴场不能包含任何一个奶牛的产奶点,但产奶点可以出在浴场的边界上。John的牛场和规划的浴场都是矩形,浴场要完全位于牛场之内,并且浴场的轮廓要与牛场的轮廓平行或者重合。要求所求浴场的面积尽可能大。
参数约定:产奶点的个数S不超过5000,牛场的范围N×M不超过30000×30000。
二、 定义和说明
首先明确一些概念。
1、 定义有效子矩形为内部不包含任何障碍点且边界与坐标轴平行的子矩形。如图所示,第一个是有效子矩形(尽管边界上有障碍点),第二个不是有效子矩形(因为内部含有障碍点)。
2、 极大有效子矩形:一个有效子矩形,如果不存在包含它且比它大的有效子矩形,就称这个有效子矩形为极大有效子矩形。(为了叙述方便,以下称为极大子矩形)
3、 定义最大有效子矩形为所有有效子矩形中最大的一个(或多个)。以下简称为最大子矩形。
三、 极大化思想
【定理1】在一个有障碍点的矩形中的最大子矩形一定是一个极大子矩形。
证明:如果最大子矩形A不是一个极大子矩形,那么根据极大子矩形的定义,存在一个包含A且比A更大的有效子矩形,这与“A是最大子矩形”矛盾,所以【定理1】成立。
四、 从问题的特征入手,得到两种常用的算法
定理1虽然很显然,但却是很重要的。根据定理1,我们可以得到这样一个解题思路:通过枚举所有的极大子矩形,就可以找到最大子矩形。下面根据这个思路来设计算法。
约定:为了叙述方便,设整个矩形的大小为n×m,其中障碍点个数为s。
算法1
算法的思路是通过枚举所有的极大子矩形找出最大子矩形。根据这个思路可以发现,如果算法中有一次枚举的子矩形不是有效子矩形、或者不是极大子矩形,那么可以肯定这个算法做了“无用功”,这也就是需要优化的地方。怎样保证每次枚举的都是极大子矩形呢,我们先从极大子矩形的特征入手。
【定理2】:一个极大子矩形的四条边一定都不能向外扩展。更进一步地说,一个有效子矩形是极大子矩形的充要条件是这个子矩形的每条边要么覆盖了一个障碍点,要么与整个矩形的边界重合。
定理2的正确性很显然,如果一个有效子矩形的某一条边既没有覆盖一个障碍点,又没有与整个矩形的边界重合,那么肯定存在一个包含它的有效子矩形。根据定理2,我们可以得到一个枚举极大子矩形的算法。为了处理方便,首先在障碍点的集合中加上整个矩形四角上的点。每次枚举子矩形的上下左右边界(枚举覆盖的障碍点),然后判断是否合法(内部是否有包含障碍点)。这样的算法时间复杂度为O(S5),显然太高了。考虑到极大子矩形不能包含障碍点,因此这样枚举4个边界显然会产生大量的无效子矩形。
考虑只枚举左右边界的情况。对于已经确定的左右边界,可以将所有处在这个边界内的点按从上到下排序,如图1中所示,每一格就代表一个有效子矩形。这样做时间复杂度为O(S3)。由于确保每次得到的矩形都是合法的,所以枚举量比前一种算法小了很多。但需要注意的是,这样做枚举的子矩形虽然是合法的,然而不一定是极大的。所以这个算法还有优化的余地。通过对这个算法不足之处的优化,我们可以得到一个高效的算法。
回顾上面的算法,我们不难发现,所枚举的矩形的上下边界都覆盖了障碍点或者与整个矩形的边界重合,问题就在于左右边界上。只有那些左右边界也覆盖了障碍点或者与整个矩形的边界重合的有效子矩形才是我们需要考察的极大子矩形,所以前面的算法做了不少“无用功”。怎么减少“无用功”呢,这里介绍一种算法(算法1),它可以用在不少此类题目上。
算法的思路是这样的,先枚举极大子矩形的左边界,然后从左到右依次扫描每一个障碍点,并不断修改可行的上下边界,从而枚举出所有以这个定点为左边界的极大子矩形。考虑如图2中的三个点,现在我们要确定所有以1号点为左边界的极大矩形。先将1号点右边的点按横坐标排序。然后按从左到右的顺序依次扫描1号点右边的点,同时记录下当前的可行的上下边界。
开始时令当前的上下边界分别为整个矩形的上下边界。然后开始扫描。第一次遇到2号点,以2号点作为右边界,结合当前的上下边界,就得到一个极大子矩形(如图3)。同时,由于所求矩形不能包含2号点,且2号点在1号点的下方,所以需要修改当前的下边界,即以2号点的纵坐标作为新的下边界。第二次遇到3号点,这时以3号点的横坐标作为右边界又可以得到一个满足性质1的矩形(如图4)。类似的,需要相应地修改上边界。以此类推,如果这个点是在当前点(确定左边界的点)上方,则修改上边界;如果在下方,则修改下边界;如果处在同一行,则可中止搜索(因为后面的矩形面积都是0了)。由于已经在障碍点集合中增加了整个矩形右上角和右下角的两个点,所以不会遗漏右边界与整个矩形的右边重合的极大子矩形(如图5)。需要注意的是,如果扫描到的点不在当前的上下边界内,那么就不需要对这个点进行处理。
这样做是否将所有的极大子矩形都枚举过了呢?可以发现,这样做只考虑到了左边界覆盖一个点的矩形,因此我们还需要枚举左边界与整个矩形的左边界重合的情况。这还可以分为两类情况。一种是左边界与整个举行的左边界重合,而右边界覆盖了一个障碍点的情况,对于这种情况,可以用类似的方法从右到左扫描每一个点作为右边界的情况。另一种是左右边界均与整个矩形的左右边界重合的情况,对于这类情况我们可以在预处理中完成:先将所有点按纵坐标排序,然后可以得到以相邻两个点的纵坐标为上下边界,左右边界与整个矩形的左右边界重合的矩形,显然这样的矩形也是极大子矩形,因此也需要被枚举到。
通过前面两步,可以枚举出所有的极大子矩形。算法1的时间复杂度是O(S2)。这样,可以解决大多数最大子矩形和相关问题了。
虽然以上的算法(算法1)看起来是比较高效的,但也有使用的局限性。可以发现,这个算法的复杂度只与障碍点的个数s有关。但对于某些问题,s最大有可能达到n×m,当s较大时,这个算法就未必能满足时间上的要求了。能否设计出一种依赖于n和m的算法呢?这样在算法1不能奏效的时候我们还有别的选择。我们再重新从最基本的问题开始研究。
算法2
首先,根据定理1:最大有效子矩形一定是一个极大子矩形。不过与前一种算法不同的是,我们不再要求每一次枚举的一定是极大子矩形而只要求所有的极大子矩形都被枚举到。看起来这种算法可能比前一种差,其实不然,因为前一种算法并不是完美的:虽然每次考察的都是极大子矩形,但它还是做了一定量的“无用功”。可以发现,当障碍点很密集的时候,前一种算法会做大量没用的比较工作。要解决这个问题,我们必须跳出前面的思路,重新考虑一个新的算法。注意到极大子矩形的个数不会超过矩形内单位方格的个数,因此我们有可能找出一种时间复杂度是O(N×M)的算法。
定义:
有效竖线:除了两个端点外,不覆盖任何障碍点的竖直线段。
悬线:上端点覆盖了一个障碍点或达到整个矩形上端的有效竖线。如图所示的三个有效竖线都是悬线。
对于任何一个极大子矩形,它的上边界上要么有一个障碍点,要么和整个矩形的上边界重合。那么如果把一个极大子矩形按x坐标不同切割成多个(实际上是无数个)与y轴垂直的线段,则其中一定存在一条悬线。而且一条悬线通过尽可能地向左右移动恰好能得到一个子矩形(未必是极大子矩形,但只可能向下扩展)。通过以上的分析,我们可以得到一个重要的定理。
【定理3】:如果将一个悬线向左右两个方向尽可能移动所得到的有效子矩形称为这个悬线所对应的子矩形,那么所有悬线所对应的有效子矩形的集合一定包含了所有极大子矩形的集合。
定理3中的“尽可能”移动指的是移动到一个障碍点或者矩形边界的位置。
根据【定理3】可以发现,通过枚举所有的悬线,就可以枚举出所有的极大子矩形。由于每个悬线都与它底部的那个点一一对应,所以悬线的个数=(n-1)×m(以矩形中除了顶部的点以外的每个点为底部,都可以得到一个悬线,且没有遗漏)。如果能做到对每个悬线的操作时间都为O(1),那么整个算法的复杂度就是O(NM)。这样,我们看到了解决问题的希望。
现在的问题是,怎样在O(1)的时间内完成对每个悬线的操作。我们知道,每个极大子矩形都可以通过一个悬线左右平移得到。所以,对于每个确定了底部的悬线,我们需要知道有关于它的三个量:顶部、左右最多能移动到的位置。对于底部为(i,j)的悬线,设它的高为hight[i,j],左右最多能移动到的位置为left[i,j],right[i,j]。为了充分利用以前得到的信息,我们将这三个函数用递推的形式给出。
对于以点(i,j)为底部的悬线:
如果点(i-1,j)为障碍点,那么,显然以(i,j)为底的悬线高度为1,而且左右均可以移动到整个矩形的左右边界,即
如果点(i-1,j)不是障碍点,那么,以(i,j)为底的悬线就等于以(i-1,j)为底的悬线+点(i,j)到点(i-1,j)的线段。因此,height[i,j]=height[i-1,j]+1。比较麻烦的是左右边界,先考虑left[i,j]。如下图所示,(i,j)对应的悬线左右能移动的位置要在(i-1,j)的基础上变化。
即left[i,j]=max
right[i,j]的求法类似。综合起来,可以得到这三个参数的递推式:
这样做充分利用了以前得到的信息,使每个悬线的处理时间复杂度为O(1)。对于以点(i,j)为底的悬线对应的子矩形,它的面积为(right[i,j]-left[i,j])*height[i,j]。
这样最后问题的解就是:
Result=
max
整个算法的时间复杂度为O(NM),空间复杂度是O(NM)。
两个算法的对比:
以上说了两种具有一定通用性的处理算法,时间复杂度分别为O(S2)和O(NM)。两种算法分别适用于不同的情况。从时间复杂度上来看,第一种算法对于障碍点稀疏的情况比较有效,第二种算法则与障碍点个数的多少没有直接的关系(当然,障碍点较少时可以通过对障碍点坐标的离散化来减小处理矩形的面积,不过这样比较麻烦,不如第一种算法好),适用于障碍点密集的情况。
五、 例题
将前面提出的两种算法运用于具体的问题。
1、 Winter Camp2002,奶牛浴场
分析:
题目的数学模型就是给出一个矩形和矩形中的一些障碍点,要求出矩形内的最大有效子矩形。这正是我们前面所讨论的最大子矩形问题,因此前两种算法都适用于这个问题。
下面分析两种算法运用在本题上的优略:
对于第一种算法,不用加任何的修改就可以直接应用在这道题上,时间复杂度为O(S2),S为障碍点个数;空间复杂度为O(S)。
对于第二种算法,需要先做一定的预处理。由于第二种算法复杂度与牛场的面积有关,而题目中牛场的面积很大(30000×30000),因此需要对数据进行离散化处理。离散化后矩形的大小降为S×S,所以时间复杂度为O(S2),空间复杂度为O(S)。说明:需要注意的是,为了保证算法能正确执行,在离散化的时候需要加上S个点,因此实际需要的时间和空间较大,而且编程较复杂。
从以上的分析来看,无论从时空效率还是编程复杂度的角度来看,这道题采用第一种算法都更优秀。
2、 OIBH模拟赛1,提高组,Candy
题意简述:(原题见论文附件)
一个被分为 n*m 个格子的糖果盒,第 i 行第 j 列位置的格子里面有 a [i,j] 颗糖。但糖果盒的一些格子被老鼠洗劫。现在需要尽快从这个糖果盒里面切割出一个矩形糖果盒,新的糖果盒不能有洞,并且希望保留在新糖果盒内的糖的总数尽量多。
参数约定:1 ≤ n,m ≤ 1000
分析
首先需要注意的是:本题的模型是一个矩阵,而不是矩形。在矩阵的情况下,由于点的个数是有限的,所以又产生了一个新的问题:最大权值子矩阵。
定义:
有效子矩阵为内部不包含任何障碍点的子矩形。与有效子矩形不同,有效子矩阵地边界上也不能包含障碍点。
有效子矩阵的权值(只有有效子矩形才有权值)为这个子矩阵包含的所有点的权值和。
最大权值有效子矩阵为所有有效子矩阵中权值最大的一个。以下简称为最大权值子矩阵。
本题的数学模型就是正权值条件下的最大权值子矩阵问题。再一次利用极大化思想,因为矩阵中的权值都是正的,所以最大权值子矩阵一定是一个极大子矩阵。所以我们只需要枚举所有的极大子矩阵,就能从中找到最大权值子矩阵。同样,两种算法只需稍加修改就可以解决本题。下面分析两种算法应用在本题上的优略:
对于第一种算法,由于矩形中障碍点的个数是不确定的,而且最大有可能达到N×M,这样时间复杂度有可能达到O(N2M2),空间复杂度为O(NM)。此外,由于矩形与矩阵的不同,所以在处理上会有一些小麻烦。
对于第二种算法,稍加变换就可以直接使用,时间复杂度为O(NM),空间复杂度为O(NM)。
可以看出,第一种算法并不适合这道题,因此最好还是采用第二种算法。
3、 Usaco Training, Section 1.5.4, Big Barn
题意简述(原题见论文附件)
Farmer John想在他的正方形农场上建一个正方形谷仓。由于农场上有一些树,而且Farmer John又不想砍这些树,因此要找出最大的一个不包含任何树的一块正方形场地。每棵树都可以看成一个点。
参数约定:牛场为N×N的,树的棵数为T。N≤1000,T≤10000。
分析:
这题是矩形上的问题,但要求的是最大子正方形。首先,明确一些概念。
1、 定义有效子正方形为内部不包含任何障碍点的子正方形
2、 定义极大有效子正方形为不能再向外扩展的有效子正方形,一下简称极大子正方形
3、 定义最大有效子正方形为所有有效子正方形中最大的一个(或多个),以下简称最大子正方形。
本题的模型有一些特殊,要在一个含有一些障碍点的矩形中求最大子正方形。这与前两题的模型是否有相似之处呢?还是从最大子正方形的本质开始分析。
与前面的情况类似,利用极大化思想,我们可以得到一个定理:
【定理4】:在一个有障碍点的矩形中的最大有效子正方形一定是一个极大有效子正方形。
根据【定理4】,我们只需要枚举出所有的极大子正方形,就可以从中找出最大子正方形。极大子正方形有什么特征呢?所谓极大,就是不能再向外扩展。如果是极大子矩形,那么不能再向外扩展的充要条件是四条边上都覆盖了障碍点(【定理2】)。类似的,我们可以知道,一个有效子正方形是极大子正方形的充要条件是它任何两条相邻的边上都覆盖了至少一个障碍点。根据这一点,可以得到一个重要的定理。
【定理5】:每一个极大子正方形都至少被一个极大子矩形包含。且这个极大子正方形一定有两条不相邻的边与这个包含它的极大子矩形的边重合。
根据【定理5】,我们只需要枚举所有的极大子矩形,并检查它所包含的极大子正方形(一个极大子矩形包含的极大子正方形都是一样大的)是否是最大的就可以了。这样,问题的实质和前面所说的最大子矩形问题是一样的,同样的,所采用的算法也是一样的。
因为算法1和算法2都枚举出了所有的极大子矩形,因此,算法1和算法2都可以用在本题上。具体的处理方法如下:对于每一个枚举出的极大子矩形,如图所示,如果它的边长为a、b,那么它包含的极大子正方形的边长即为min(a,b)。
考虑到N和T的大小不同,所以不同的算法会有不同的效果。下面分析两种算法应用在本题上的优略。
对于第一种算法,时间复杂度为O(T2),对于第二种算法,时间复杂度为O(N2)。因为N<T,所以从时间复杂度的角度看,第二种算法要比第一种算法好。考虑到两个算法的空间复杂度都可以承受,所以选择第二种算法较好些。
以下是第一种和第二种算法编程实现后在USACO Training Program Gateway上的运行时间。可以看出,在数据较大时,算法2的效率比算法1高。
算法1:
Test 1: 0.009375
Test 2: 0.009375
Test 3: 0.009375
Test 4: 0.009375
Test 5: 0.009375
Test 6: 0.009375
Test 7: 0.021875
Test 8: 0.025
Test 9: 0.084375
Test 10: 0.3875
Test 11: 0.525
Test 12: 0.5625
Test 13: 0.690625
Test 14: 0.71875
Test 15: 0.75 算法2:
Test 1: 0.009375
Test 2: 0.009375
Test 3: 0.009375
Test 4: 0.009375
Test 5: 0.009375
Test 6: 0.00625
Test 7: 0.009375
Test 8: 0.009375
Test 9: 0.0125
Test 10: 0.021875
Test 11: 0.028125
Test 12: 0.03125
Test 13: 0.03125
Test 14: 0.03125
Test 15: 0.034375
以上,利用极大化思想和前面设计的两个算法,通过转换模型,解决了三个具有一定代表性的例题。解题的关键就是如何利用极大化思想进行模型转换和如何选择算法。
五、小结
设计算法要从问题的基本特征入手,找出解题的突破口。本文介绍了两种适用于大部分最大子矩形问题及相关变型问题的算法,它们设计的突破口就是利用了极大化思想,找到了枚举极大子矩形这种方法。
在效率上,两种算法对于不同的情况各有千秋。一个是针对障碍点来设计的,因此复杂度与障碍点有关;另一个是针对整个矩形来设计的,因此复杂度与矩形的面积有关。虽然两个算法看起来有着巨大的差别,但他们的本质是相通的,都是利用极大化思想,从枚举所有的极大有效子矩形入手,找出解决问题的方法。
需要注意的是,在解决实际问题是仅靠套用一些现有算法是不够的,还需要对问题进行全面、透彻的分析,找出解题的突破口。
此外,如果采用极大化思想,前面提到的两种算法的复杂度已经不能再降低了,因为极大有效子矩形的个数就是O(NM)或O(S2)的。如果采用其他算法,理论上是有可能进一步提高算法效率,降低复杂度的。
七、 附录:
1、几个例题的原题。 见论文附件.doc
2、例题的程序。 见论文附件.doc
说明:所有程序均在Free Pascal IDE for Dos, Version 0.9.2上编译运行
参考书目
1、 信息学奥林匹克 竞赛指导
----1997~1998竞赛试题解析
吴文虎 王建德 着
2、 IOI99中国集训队优秀论文集
3、 信息学奥林匹克(季刊)
4、 《金牌之路 竞赛辅导》
江文哉主编 陕西师范大学出版社出版