数学建模算法合集之《动态规划的特点及其应用》

更新时间:2024-06-25 07:01:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

动态规划的特点及其应用

目 录 (点击进入) §1动态规划的本质 §1.1多阶段决策问题 §1.2阶段与状态 §1.3决策和策略 §1.4最优化原理与无后效性 §1.5最优指标函数和规划方程 §2动态规划的设计与实现 §2.1动态规划的多样性 §2.2动态规划的模式性 §2.3动态规划的技巧性 §3动态规划与一些算法的比较 §3.1动态规划与递推 §3.2动态规划与搜索 §3.3动态规划与网络流 §4结语 【附录:部分试题与源程序】 1.“花店橱窗布置问题”试题 2.“钉子与小球”试题 3.例2“花店橱窗布置问题”方法1的源程序 4.例2“花店橱窗布置问题”方法2的源程序 5.例3“街道问题”的扩展 6.例4“mod 4最优路径问题”的源程序 7.例5“钉子与小球”的源程序 8.例6的源程序,“N个人的街道问题” 【参考文献】 第 1 页 共 29页

【摘要】

动态规划是信息学竞赛中的常见算法,本文的主要内容就是分析它的特点。

文章的第一部分首先探究了动态规划的本质,因为动态规划的特点是由它的本质所决定的。第二部分从动态规划的设计和实现这两个角度分析了动态规划的多样性、模式性、技巧性这三个特点。第三部分将动态规划和递推、搜索、网络流这三个相关算法作了比较,从中探寻动态规划的一些更深层次的特点。

文章在分析动态规划的特点的同时,还根据这些特点分析了我们在解题中应该怎样利用这些特点,怎样运用动态规划。这对我们的解题实践有一定的指导意义 【正文】

动态规划是编程解题的一种重要的手段,在如今的信息学竞赛中被应用得越来越普遍。最近几年的信息学竞赛,不分大小,几乎每次都要考察到这方面的内容。因此,如何更深入地了解动态规划,从而更为有效地运用这个解题的有力武器,是一个值得深入研究的问题。

要掌握动态规划的应用技巧,就要了解它的各方面的特点。首要的,是要深入洞悉动态规划的本质。

§1动态规划的本质

动态规划是在本世纪50年代初,为了解决一类多阶段决策问题而诞生的。那么,什么样的问题被称作多阶段决策问题呢?

§1.1多阶段决策问题

说到多阶段决策问题,人们很容易举出下面这个例子。

[例1] 多段图中的最短路径问题:在下图中找出从A1到D1的最短路径。

7 C1 B 1 4 3 8 6 1 C 2 7 A 5 4 6 B2 C3 5 页 共 29页 第 2 6

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={C1,C2,C3}。

每个阶段的状态都是由以前阶段的状态以某种方式“变化”而来,这种“变化”称为状态转移(暂不定义)。上例中C3点可以从B1点过来,也可以从B2点过来,从阶段2的B1或B2状态走到阶段3的C3状态就是状态转移。状态转移是导出状态的途径,也是联系各阶段的途径。

说到这里,可以提出应用动态规划的一个重要条件。那就是将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的发展,而只能通过当前的这个状态。换句话说,每个状态都是“过去历史的一个完整总结[1]”。这就是无后效性。对这个性质,下文还将会有解释。

§1.3决策和策略

上面的阶段与状态只是多阶段决策问题的一个方面的要素,下面是另一个方面的

第 3 页 共 29页

要素——决策。

决策:当各段的状态取定以后,就可以做出不同的决定,从而确定下一阶段的状态,这种决定称为决策。表示决策的变量,称为决策变量,常用uk(sk)表示第k阶段当状态为sk时的决策变量。在实际问题中,决策变量的取值往往限制在一定范围内,我们称此范围为允许决策集合。常用Dk(sk)表示第k阶段从状态sk出发的允许决策集合。显然有uk(sk) ?Dk(sk)。[1]

决策是问题的解的属性。决策的目的就是“确定下一阶段的状态”,还是回到上例,从阶段2的B1状态出发有三条路,也就是三个决策,分别导向阶段3的C1、C2、C3三个状态,即D2(B1)={C1,C2,C3}。

有了决策,我们可以定义状态转移:动态规划中本阶段的状态往往是上一阶段和上一阶段的决策结果,由第k段的状态sk和本阶段的决策uk确定第k+1段的状态sk+1的过程叫状态转移。状态转移规律的形式化表示sk+1=Tk(sk,uk)称为状态转移方程。 这样看来,似乎决策和状态转移有着某种联系。我的理解,状态转移是决策的目的,决策是状态转移的途径。

各段决策确定后,整个问题的决策序列就构成一个策略,用p1,n={u1(s1),u2(s2),…, un(sn)}表示。对每个实际问题,可供选择的策略有一定范围,称为允许策略集合,记作P1,n,使整个问题达到最有效果的策略就是最优策略。[1]

说到这里,又可以提出运用动态规划的一个前提。即这个过程的最优策略应具有这样的性质:无论初始状态及初始决策如何,对于先前决策所形成的状态而言,其以后的所有决策应构成最优策略[1]。这就是最优化原理。简言之,就是“最优策略的子策略也是最优策略”。

§1.4最优化原理与无后效性

这里,我把最优化原理定位在“运用动态规划的前提”。这是因为,是否符合最优化原理是一个问题的本质特征。对于不满足最优化原理的一个多阶段决策问题,整体上的最优策略p1,n同任何一个阶段k上的决策uk或任何一组阶段k1…k2上的子策略pk1,k2都不存在任何关系。如果要对这样的问题动态规划的话,我们从一开始所作的划分阶段等努力都将是徒劳的。

而我把无后效性定位在“应用动态规划的条件”,是因为动态规划是按次序去求每阶段的解,如果一个问题有后效性,那么这样的次序便是不合理的。但是,我们可以通过重新划分阶段,重新选定状态,或者增加状态变量的个数等手段,来是问题满足无后效性这个条件。说到底,还是要确定一个“序”。 在信息学的多阶段决策问题中,绝大部分都是能够满足最优化原理的,但它们往往会在后效性这一点上来设置障碍。所以在解题过程中,我们会特别关心“序”。对于有序的问题,就会考虑到动态规划;对于无序的问题,也会想方设法来使其有序。

§1.5最优指标函数和规划方程

最优指标函数:用于衡量所选定策略优劣的数量指标称为指标函数,最优指标函数记为fk(sk),它表示从第k段状态sk采用最优策略p*k,n到过程终止时的最佳效益值[1]。

第 4 页 共 29页

最优指标函数其实就是我们真正关心的问题的解。在上面的例子中,f2(B1)就表示从B1点到终点D1点的最短路径长度。我们求解的最终目标就是f1(A1)。

最优指标函数的求法一般是一个从目标状态出发的递推公式,称为规划方程:

fk(sk)?optuk?Dk(sk)g?fk?1?Tk(sk,uk)?,uk?

其中sk是第k段的某个状态,uk是从sk出发的允许决策集合Dk(sk)中的一个决策,Tk(sk,uk)是由sk和uk所导出的第k+1段的某个状态sk+1,g(x,uk)是定义在数值x和决策uk上的一个函数,而函数opt表示最优化,根据具体问题分别表为max或min。

fn(sn)?某个初始值,称为边界条件。

上例中的规划方程就是:

fk(sk)?min从sk出发的某条边uk?fk?1(uk指向的点sk?1)?边uk的长度?

边界条件为f4(D1)?0

这里是一种从目标状态往回推的逆序求法,适用于目标状态确定的问题。在我们的信息学问题中,也有很多有着确定的初始状态。当然,对于初始状态确定的问题,我们也可以采用从初始状态出发往前推的顺序求法。事实上,这种方法对我们来说要更为直观、更易设计一些,从而更多地出现在我们的解题过程中。

我们本节所讨论的这些理论虽然不是本文的主旨,但是却对下面要说的动态规划的特点起着基础性的作用。

§2动态规划的设计与实现

上面我们讨论了动态规划的一些理论,本节我们将通过几个例子中,动态规划的设计与实现,来了解动态规划的一些特点。

§2.1动态规划的多样性

[例2] 花店橱窗布置问题(IOI99)试题见附录

本题虽然是本届IOI中较为简单的一题,但其中大有文章可作。说它简单,是因为它有序,因此我们一眼便可看出这题应该用动态规划来解决。但是,如何动态规划呢?如何划分阶段,又如何选择状态呢?

<方法1>以花束的数目来划分阶段。在这里,阶段变量k表示的就是要布置的花束数目(前k束花),状态变量sk表示第k束花所在的花瓶。而对于每一个状态sk,决策就是第k-1束花应该放在哪个花瓶,用uk表示。最优指标函数fk(sk)表示前k束花,其中第k束插在第sk个花瓶中,所能取得的最大美学值。

状态转移方程为 sk?1?uk

第 5 页 共 29页

规划方程:fk(sk)?maxfk?1(sk?uk)?Datak(sk)

uk?0,1边界条件:fk(0)?0fk(k?1)?0(0?k?总行数)

这是一个比较简单的最优化问题,我们还可以把这个问题改成一个更加简单的整数统计问题:求顶点到每一点的路径总数。把这个总数用fk(sk)表示,那么递推公式就是:

1fk(sk)??uk?0fk?1(sk?uk)

在这里,虽然求和公式只有两项,但我们仍然用∑的形式表示,就是为了突出这个递推公式和上面的规划方程的相似之处。这两个公式的边界条件都是一模一样的。 再回到我们上面的“钉子与小球”问题,这是一个概率统计问题。我们继续沿用上面的思想,用fk(sk)表示小球落到第k行第sk个钉子上的概率,则递推公式如下:

1fk(sk)??uk?0fk?1(sk?uk)?Exist2k?1(sk?uk)?fk?2(sk?1)?(1?Existk?2(sk?1))

(这里函数Existk(sk)表示第k行第sk个钉子是否存在,存在则取1,不存在则取0)

边界条件

f1(1)?1fk(0)?0fk(k?1)?0(1?k?总行数)

可以看出这个公式较之上面的两个式子虽然略有变化,但是其基本思想还是类似的。在解这个问题的过程中,我们再次运用了动态规划的思想。

一般说来,很多最优化问题都有着对应的计数问题;反过来,很多计数问题也有着对应的最优化问题。因此,我们在遇到这两类问题时,不妨多联系、多发展,举一反三,从比较中更深入地理解动态规划的思想。

其实递推和动态规划这两种方法的思想本来就很相似,也不必说是谁借用了谁的思想。关键在于我们要掌握这种思想,这样我们无论在用动态规划法解最优化问题,或是在用递推法解判定型、计数问题时,都能得心应手、游刃有余了。

§3.2动态规划与搜索

——动态规划是高效率、高消费算法

同样是解决最优化问题,有的题目我们采用动态规划,而有的题目我们则需要用搜索。这其中有没有什么规则呢?

我们知道,撇开时空效率的因素不谈,在解决最优化问题的算法中,搜索可以说是“万能”的。所以动态规划可以解决的问题,搜索也一定可以解决。

把一个动态规划算法改写成搜索是非常方便的,状态转移方程、规划方程以及边界条件都可以直接“移植”,所不同的只是求解顺序。动态规划是自底向上的递推求

第 11 页 共 29页

解,而搜索则是自顶向下的递归求解(这里指深度搜索,宽度搜索类似)。

反过来,我们也可以把搜索算法改写成动态规划。状态空间搜索实际上是对隐式图中的点进行枚举,这种枚举是自顶向下的。如果把枚举的顺序反过来,变成自底向上,那么就成了动态规划。(当然这里有个条件,即隐式图中的点是可排序的,详见下一节。) 正因为动态规划和搜索有着求解顺序上的不同,这也造成了它们时间效率上的差别。在搜索中,往往会出现下面的情况:

A1 B1 C1 C2 B2 C3 C1 B1 C2 C2 A1 B2 C3 C1 B1 C2 A1 B2 C3 (a) (b) (c)

对于上图(a)这样几个状态构成的一个隐式图,用搜索算法就会出现重复,如上图(b)所示,状态C2被搜索了两次。在深度搜索中,这样的重复会引起以C2为根整个的整个子搜索树的重复搜索;在宽度搜索中,虽然这样的重复可以立即被排除,但是其时间代价也是不小的。而动态规划就没有这个问题,如上图(c)所示。

一般说来,动态规划算法在时间效率上的优势是搜索无法比拟的。(当然对于某些题目,根本不会出现状态的重复,这样搜索和动态规划的速度就没有差别了。)而从理论上讲,任何拓扑有序(现实中这个条件常常可以满足)的隐式图中的搜索算法都可以改写成动态规划。但事实上,在很多情况下我们仍然不得不采用搜索算法。那么,动态规划算法在实现上还有什么障碍吗?

A1 B1 C1 C2 B2 C3 B1 C2 C2 A1 B2 C1 B1 C2 A1 B2 C3 (a) (b) (c)

考虑上图(a)所示的隐式图,其中存在两个从初始状态无法达到的状态。在搜索算法中,这样的两个状态就不被考虑了,如上图(b)所示。但是动态规划由于是自底向上求解,所以就无法估计到这一点,因而遍历了全部的状态,如上图(c)所示。 一般说来,动态规划总要遍历所有的状态,而搜索可以排除一些无效状态。更重要的事搜索还可以剪枝,可能剪去大量不必要的状态,因此在空间开销上往往比动态规划要低很多。

如何协调好动态规划的高效率与高消费之间的矛盾呢?有一种折衷的办法就是记忆化算法。记忆化算法在求解的时候还是按着自顶向下的顺序,但是每求解一个状态,就将它的解保存下来,以后再次遇到这个状态的时候,就不必重新求解了。这种方法综合了搜索和动态规划两方面的优点,因而还是很有实用价值的。

第 12 页 共 29页

§3.3动态规划与网络流

——动态规划是易设计易实现算法

由于图的关系复杂而无序,一般难以呈现阶段特征(除了特殊的图如多段图,或特殊的分段方法如Floyd),因此动态规划在图论中的应用不多。但有一类图,它的点却是有序的,这就是有向无环图。

在有向无环图中,我们可以对点进行拓扑排序,使其体现出有序的特征,从而据此划分阶段。在有向无还图中求最短路径的算法[4],已经体现出了简单的动态规划思想。但动态规划在图论中还有更有价值的应用。下面先看一个例子。

[例6] N个人的街道问题:在街道问题(参见例3)中,若有N个人要从左下角走

向右上角,要求他们走过的边的总长度最大。当然,这里每个人也只能向右或向上走。下面是一个样例,左图是从出发地到目的地的三条路径,右图是他们所走过的边,这些边的总长度为5 + 4 + 3 + 6 + 3 + 3 + 5 + 8 + 8 + 7 + 4 + 5 + 9 + 5 + 3 = 78(不一定是最大)。

3 7 4 8 7 6 3 5 3 4 6 3 5 2 8 5 9 4 3 6 3 5 8 7 4 3 7 5 4 6 2 3 7 4 8 7 6 3 5 3 4 6 3 5 2 8 5 9 4 3 6 3 5 8 7 4 3 7 5 4 6 2 这个题目是对街道问题的又一次扩展。仿照街道问题的解题方法,我们仍然可以用动态规划来解决本题。不过这一次是N个人同时走,状态变量也就需要用N维来表示,。相应的,决策变量也要变成N维,uk=(uk,1,uk,2,…,uk,N)。状态转移方程不需要做什么改动:

??sk?1,i?sk,i?1?uk,i???sk?1,i?sk,i?uk,i(k?row)(k?row)(1?i?N)

在写规划方程时,需要注意在第k阶段,N条路径所走过的边的总长度的计算,

在这里我就用gk(sk,uk)来表示了:

fk(sk)?maxuk,i?0,1(1?i?N)?fk?1?Tk(sk,uk)??gk(sk,uk)?

边界条件为f1?(1,1,?,1)??0

可见将原来的动态规划算法移植到这个问题上来,在理论上还是完全可行的。但是,现在的这个动态规划算法的时空复杂度已经是关于N的指数函数,只要N稍微大一点,这个算法就不可能实现了。

下面我们换一个思路,将N条路径看成是网络中一个流量为N的流,这样求解的目标就是使这个流的费用最大。但是本题又不同于一般的费用流问题,在每一条边e

第 13 页 共 29页

上的流费用并不是流量和边权的乘积f(e)?w(e),而是用下式计算:

?w(e)??0f(e)?0f(e)?0

为了使经典的费用流算法适用于本题,我们需要将模型稍微转化一下:

w0 c1=1 w1=w0 c2=∞ w2=0 如图,将每条边拆成两条。拆开后一条边上有权,但是容量限制为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的弧尾结点

??fk?Tk(sk,uk)??f(s)?opt规划方程为kk?uk,i?E(sk,i)??w(u) ?k??i?1?N边界条件为f1?(1,1,?1)??0

但是,由于动态规划算法是指数级算法,因而在实现中的局限性很大,仅可用于

一些N非常小的题目。然而在竞赛解题中,比如上面说到的IOI97以及99冬令营测试时,我们使用动态规划的倾向性很明显(“障碍物探测器”中,我们用的是贪心策

[8]

略,求N=1或N=2时的局部最优解)。这主要有两个原因:

一.虽然网络流有着经典的算法,但是在竞赛中不可能出现经典的问题。如果要

运用网络流算法,则需要经过一番模型转化,有时这个转化还是相当困难的。因此在算法的设计上,灵活巧妙的动态规划算法反而要更为简单一些。 二.网络流算法实现起来很繁,这是被人们公认的。因而在竞赛的紧张环境中,

实现起来有一定模式的动态规划算法又多了一层优势。 正由于动态规划算法在设计和实现上的简便性,所以在N不太大时,也就是在动

第 14 页 共 29页

态规划可行的情况下,我们还是应该尽量运用动态规划。

§4结语

本文的内容比较杂,是我几年来对动态规划的参悟理解、心得体会。虽然主要的篇幅讲的都是理论,但是根本的目的还是指导实践。

动态规划,据我认为,是当今信息学竞赛中最灵活、也最能体现解题者水平的一类解题方法。本文内容虽多,不能涵盖动态规划之万一。“纸上得来终觉浅,绝知此事要躬行。”要想真正领悟、理解动态规划的思想,掌握动态规划的解题技巧,还需要在实践中不断地挖掘、探索。实践得多了,也就能体会到渐入佳境之妙了。

动态规划, 算法之常, 运用之妙, 存乎一心。

【附录:部分试题与源程序】

1. “花店橱窗布置问题”试题 LITTLE SHOP OF FLOWERS

PROBLEM

You want to arrange the window of your flower shop in a most pleasant way. You have F bunches of flowers, each being of a different kind, and at least as many vases ordered in a row. The vases are glued onto the shelf and are numbered consecutively 1 through V, where V is the number of vases, from left to right so that the vase 1 is the leftmost, and the vase V is the rightmost vase. The bunches are moveable and are uniquely identified by integers between 1 and F. These id-numbers have a significance: They determine the required order of appearance of the flower bunches in the row of vases so that the bunch i must be in a vase to the left of the vase containing bunch j whenever i < j. Suppose, for example, you have bunch of azaleas (id-number=1), a bunch of begonias (id-number=2) and a bunch of carnations (id-number=3). Now, all the bunches must be put into the vases keeping their id-numbers in order. The bunch of azaleas must be in a vase to the left of begonias, and the bunch of begonias must be in a vase to the left of carnations. If there are more vases than bunches of flowers then the excess will be left empty. A vase can hold only one bunch of flowers.

Each vase has a distinct characteristic (just like flowers do). Hence, putting a bunch of flowers in a vase results in a certain aesthetic value, expressed by an integer. The aesthetic values are presented in a table as shown below. Leaving a vase empty has an aesthetic value of 0.

VASES 1 2 3 4 5 第 15 页 共 29页

1 (azaleas) 7 23 -5 -24 16 Bunches 2 (begonias) 5 21 -4 10 23 3 (carnations) -21 5 -4 -20 20 According to the table, azaleas, for example, would look great in vase 2, but they would look awful in vase 4.

To achieve the most pleasant effect you have to maximize the sum of aesthetic values for the arrangement while keeping the required ordering of the flowers. If more than one arrangement has the maximal sum value, any one of them will be acceptable. You have to produce exactly one arrangement.

ASSUMPTIONS

1 <= F <= 100 where F is the number of the bunches of flowers. The bunches are numbered 1 through F.

F <= V <= 100 where V is the number of vases.

-50 <= Aij <= 50 where Aij is the aesthetic value obtained by putting the flower bunch i into the vase j.

INPUT

The input is a text file named flower.inp. The first line contains two numbers: F, V.

The following F lines: Each of these lines contains V integers, so that Aij is given as the jth number on the (i+1)st line of the input file.

OUTPUT

The output must be a text file named flower.out consisting of two lines: The first line will contain the sum of aesthetic values for your arrangement. The second line must present the arrangement as a list of F numbers, so that the k’th number on this line identifies the vase in which the bunch k is put.

EXAMPLE

flower.inp: 3 5

7 23 -5 -24 16 5 21 -4 10 23 -21 5 -4 -20 20

flower.out: 53 2 4 5

EVALUATION

Your program will be allowed to run 2 seconds.

No partial credit can be obtained for a test case.

第 16 页 共 29页

2. “钉子与小球”试题

有一个三角形木板,竖直立放,上面钉着n(n+1)/2颗钉子,还有(n+1)个格子(当n=5时如图1)。每颗钉子和周围的钉子的距离都等于d,每个格子的宽度也都等于d,且除了最左端和最右端的格子外每个格子都正对着最下面一排钉子的间隙。

让一个直径略小于d的小球中心正对着最上面的钉子在板上自由滚落,小球每碰到一个钉子都可能落向左边或右边(概率各1/2),且球的中心还会正对着下一颗将要碰上的钉子。例如图2就是小球一条可能的路径。

in我们知道小球落在第i个格子中的概率pi?Cn2?n!i!(n?i)!n其中i为格子2,

的编号,从左至右依次为0,1,...,n。

现在的问题是计算拔掉某些钉子后,小球落在编号为m的格子中的概率pm。假定最下面一排钉子不会被拔掉。例如图3是某些钉子被拔掉后小球一条可能的路径。

图1??????????????? ??????????图2?????????? ???????????????图3

输入

第1行为整数n(2<=n<=50)和m(0<=m<=n)。

以下n行依次为木板上从上至下n行钉子的信息,每行中‘*’表示钉子还在,‘.’?表示钉子被拔去,注意在这n行中空格符可能出现在任何位置。

输出

仅一行,是一个既约分数(0写成0/1),为小球落在编号为m的格子中的概率pm。 既约分数的定义:A/B是既约分数,当且仅当A、B为正整数且A和B没有大于1的公因子。

样例输入 5 2 * * . * * * * . * * * * * * *

样例输出 7/16

3. 例2“花店橱窗布置问题”方法1的源程序

第 17 页 共 29页

program IOI99_LittleShopOfFlowers;

var F,V :byte; {花束和花瓶的数目} A :array [1..100,1..100] of shortint; {A[i,j]花束i放在花瓶j中的美学值} Best :array [0..100,0..100] of integer;

{Best[i,j]前i束花放在前j个花瓶中的最优解}

{Previous[i,j]在Best[i,j]的最优解中,花束i-1的位置} Previous :array [1..100,1..100] of byte;

procedure ReadIn; {读入} var i,j :byte; begin reset(input);

readln(F,V);

for i:=1 to F do begin for j:=1 to V do read(A[i,j]);

readln; end;

close(input); end;

procedure Work; {规划主过程} var i,j,t

:byte;

begin fillchar(Best[0],sizeof(Best[0]),0); {边界条件} for i:=1 to F do

for j:=i to V+i-F do begin

Best[i,j]:=low(Best[i,j]); for t:=i-1 to j-1 do {枚举花束i-1的位置}

if Best[i-1,t]>Best[i,j] then begin {更新当前最优解}

Best[i,j]:=Best[i-1,t]; Previous[i,j]:=t; end;

inc(Best[i,j],A[i,j]);

end;

end;

procedure Print; {打印最优解}

第 18 页 共 29页

var

i,j :byte; Put :array [1..100] of byte; begin i:=F;

begin assign(input,'flower.inp'); assign(output,'flower.out'); ReadIn;

Work; Print;

for j:=F+1 to V do {选择全局最优解}

if Best[F,j]>Best[F,i] then i:=j; {i最后一束花的位置} rewrite(output); writeln(Best[F,i]); for j:=F downto 1 do begin

Put[j]:=i;

i:=Previous[j,i];

end;

for i:=1 to F do write(Put[i],' '); writeln;

close(output);

end;

end.

4. 例2“花店橱窗布置问题”方法2的源程序

program IOI99_LittleShopOfFlowers; var F,V :byte; {花束和花瓶的数目}

A :array [1..100,1..100] of shortint; {A[i,j]花束i放在花瓶j中的美学值} Best :array [0..100,0..100] of integer; {Best[i,j]前i束花放在前j个花瓶中的最优解} Choice :array [0..100,0..100] of boolean; {Choice[i,j] 花束i是否放在花瓶j中}

{读入}

procedure ReadIn; var

i,j :byte; reset(input); readln(F,V); begin

第 19 页 共 29页

for i:=1 to F do begin for j:=1 to V do read(A[i,j]); readln; end;

close(input);

end;

procedure Work; {规划主过程} var i,j :byte;

begin fillchar(Best[0],sizeof(Best[0]),0); {边界条件}

for i:=1 to F do begin

Best[i,i]:=Best[i-1,i-1]+A[i,i]; {唯一的选择} Choice[i,i]:=true;

for j:=i+1 to V+i-F do begin

Choice[i,j]:=Best[i-1,j-1]+A[i,j]>Best[i,j-1]; if Choice[i,j] then Best[i,j]:=Best[i-1,j-1]+A[i,j] {i放在j中} else Best[i,j]:=Best[i,j-1]; {i不放在j中} end;

end; end;

procedure Print; {打印最优解} var

i,j :byte;

Put :array [1..100] of byte; {Put[i]花束i所在的花瓶}

begin rewrite(output);

writeln(Best[F,V]); i:=F;

for j:=V downto 1 do {倒推求Put}

if Choice[i,j] then begin

Put[i]:=j; dec(i);

end;

for i:=1 to F do begin

第 20 页 共 29页

write(Put[i]);

if i

writeln;

close(output);

end;

begin assign(input,'flower.inp');

assign(output,'flower.out'); ReadIn; Work;

Print; end.

5. 例3“街道问题”的扩展,找两条不重叠的最短路径

program N_Street; const

MaxSize=90; {地图的最大尺寸} type var

Row,Col :byte; {地图的行数和列数} Best,B1 :TPlanarArr; {动态规划中本阶段和上阶段的最优指标函数} Street :array [0..1] of TPlanarArr; {地图中横向和纵向的边}

{读入}

TPlanarArr=array [1..MaxSize,1..MaxSize] of integer;

procedure ReadIn; var i,j :byte; begin reset(input);

readln(Row,Col); for i:=1 to Row do

begin for j:=1 to Col-1 do read(Street[0,i,j]); readln; end;

for j:=1 to Col do begin

for i:=1 to Row-1 do

read(Street[1,i,j]); readln;

end;

close(input);

第 21 页 共 29页

end;

procedure Work; {规划过程,和文章中不同,这里是逆序求解} var k, {阶段}

s1,s2, {状态(二维)} u1,u2, {决策(二维)}

t1,t2, {由s和t导出的上阶段的状态(二维)} m, {本阶段的状态总数} m1,m2

:byte; {Row和Col当中较小的数和较大的数}

e1,e2 :integer; {u1,u2对应的两条边长}

function GetET(s,u:byte; var e:integer; var t:byte):boolean; {求e,t} begin GetET:=false;

if (k>=Row) and (s=1) and (u=1) or (k>=Col) and (s=m) and (u=0) then exit; {判断越界} GetET:=true;

if k

e:=Street[u,k+1-s,s];

t:=s+1-u; end

else begin e:=Street[u,Row+1-s,k-Row+s]; t:=s-u; end;

end; begin if Row

then begin m1:=Row; m2:=Col; end

else begin

m1:=Col; m2:=Row; end;

Best[1,1]:=0;

for k:=Row+Col-2 downto 1 do {逆序求解} begin

if k

else if k>m2 then m:=Row+Col-k else m:=m1;

第 22 页 共 29页

B1:=Best;

for s1:=1 to m do for s2:=1 to m do begin Best[s1,s2]:=$7000; {初始化为一个较大的数}

for u1:=0 to 1 do

if GetET(s1,u1,e1,t1) then

for u2:=0 to 1 do if ((s1<>s2) or (u1<>u2)) and

GetET(s2,u2,e2,t2) and

(B1[t1,t2]+e1+e2

Best[s1,s2]:=B1[t1,t2]+e1+e2; {更新最优解}

end;

end;

end;

begin assign(input,'input.txt'); assign(output,'output.txt');

ReadIn; Work;

rewrite(output); writeln(Best[1,1]); close(output); end.

6. 例4“mod 4最优路径问题”的源程序

program BestPath_mod4; const MaxN=100; {点数上限} MaxE=100; {两点之间的边数上限} var N

E

:byte; {点数}

:array [1..MaxN,0..MaxE] of integer; {E[i,0]第i点到第i+1点之间的边数}

{E[i,j]第i点到第i+1点之间的第j条边长}

Best :array [1..MaxN,0..3] of boolean; {Best[k,s]从第1点到第k点长度mod 4的值为s的路径是否存在}

{读入}

procedure ReadIn; var

i,j :byte; reset(input); readln(N); begin

第 23 页 共 29页

for i:=1 to N-1 do begin read(E[i,0]); for j:=1 to E[i,0] do read(E[i,j]);

readln; end;

close(input); end;

procedure Work; {主递推过程} var k,s,u begin

:byte; {阶段、状态、决策}

fillchar(Best,sizeof(Best),false); Best[1,0]:=true; {边界条件} for k:=2 to N do for s:=0 to 3 do for u:=1 to E[k-1,0] do

Best[k,s]:=Best[k,s] or Best[k-1,($10000+s-E[k-1,u]) mod 4];

{加上10000(h)为防止出现负数}

end;

procedure Print; {这里只输出最优路径mod 4的值,路径就不输出了} var i begin

:byte;

for i:=0 to 3 do if Best[N,i] then break; rewrite(output); writeln(i);

close(output); end;

begin

assign(input,'input.txt'); assign(output,'output.txt'); ReadIn; Work; Print;

end.

7. 例5“钉子与小球”的源程序

{$A+,B-,D+,E+,F-,G+,I+,L+,N+,O-,P-,Q-,R-,S-,T-,V+,X+,Y+}

第 24 页 共 29页

{$M 65520,0,655360}

program Ball; var N,M :byte; Nail :array [1..50,1..50] of byte;

procedure ReadIn; var

i,j :byte;

:char;

c begin

Poss :array [0..50,0..50] of comp;

reset(input); readln(N,M);

for i:=1 to N do for j:=1 to i do

begin repeat read(c);

until c in ['*','.']; if c='*'

then Nail[i,j]:=1 else Nail[i,j]:=0; end;

close(input);

end;

procedure Work; var i,j :byte;

begin fillchar(Poss,sizeof(Poss),0);

Poss[0,0]:=1; for i:=1 to N do

Poss[0,0]:=Poss[0,0]*2; for i:=0 to N-1 do

for j:=0 to i do

if Nail[i+1,j+1]=0

then Poss[i+2,j+1]:=Poss[i+2,j+1]+Poss[i,j] else begin

Poss[i+1,j]:=Poss[i+1,j]+Poss[i,j]/2; Poss[i+1,j+1]:=Poss[i+1,j+1]+Poss[i,j]/2; end;

end;

第 25 页 共 29页

procedure Print; var a,b :comp; begin;

begin assign(input,'input.txt'); assign(output,'output.txt'); ReadIn;

Work; Print;

rewrite(output); a:=Poss[N,M]; if a=0

then writeln('0/1') else begin

b:=a/2;

while b*2=a do begin a:=b; b:=a/2;

Poss[0,0]:=Poss[0,0]/2; end;

writeln(a:0:0,'/',Poss[0,0]:0:0); end;

close(output);

end;

end.

8. 例6的源程序,“N个人的街道问题”,网络流算法

{$A+,B-,D+,E+,F-,G+,I+,L+,N+,O-,P-,Q+,R+,S+,T-,V-,X+,Y+} {$M 65520,0,655360} program N_Street;

const MaxSize=100; {地图的最大尺寸} var

Row,Col, {地图的行数和列数} N :byte; {人数N} Horiz, {横向的边} Vert : {纵向的边} F

array [1..MaxSize,1..MaxSize] of word; :array [1..MaxSize,1..MaxSize,0..1] of byte;

{F[i,j,d]从点(i,j)向方向d去的流量}

Result :word; {最优结果}

第 26 页 共 29页

procedure ReadIn; var i,j :byte; begin reset(input);

{读入}

readln(Row,Col,N); for i:=1 to Row do begin for j:=1 to Col-1 do

read(Horiz[i,j]);

readln; end;

for j:=1 to Col do begin

for i:=1 to Row-1 do read(Vert[i,j]);

readln; end;

close(input);

end;

procedure Work; var k,i,j :byte; W :array [1..MaxSize,1..MaxSize] of word;

P :array [1..MaxSize,1..MaxSize] of byte;

procedure GetMaxPath; {求最大费用路,用Bellman-Ford迭代法} var i,j :byte; t :integer; Finish :boolean; begin

{迭代终止标志}

fillchar(W,sizeof(W),0); repeat

Finish:=true;

for i:=Row downto 1 do

for j:=Col downto 1 do begin if i

begin

{横向边} t:=W[i+1,j];

if F[i,j,1]=0 then inc(t,Vert[i,j]); if t>W[i,j] then

第 27 页 共 29页

begin

W[i,j]:=t; P[i,j]:=1; Finish:=false; end;

if F[i,j,1]>0 then begin

{横的逆向边} t:=W[i,j];

if F[i,j,1]=1 then

dec(t,Vert[i,j]); if t>W[i+1,j] then begin

W[i+1,j]:=t; P[i+1,j]:=3;

Finish:=false; end;

end; end;

if j

{纵向边} t:=W[i,j+1];

if F[i,j,0]=0 then inc(t,Horiz[i,j]); if t>W[i,j] then begin

W[i,j]:=t; P[i,j]:=0;

Finish:=false; end;

if F[i,j,0]>0 then begin {纵的逆向边} t:=W[i,j];

if F[i,j,0]=1 then dec(t,Horiz[i,j]); if t>W[i,j+1] then begin

W[i,j+1]:=t; P[i,j+1]:=2; Finish:=false;

end;

end;

end; end; until Finish;

第 28 页 共 29页

end;

begin fillchar(F,sizeof(F),0); Result:=0; for k:=1 to N do{求的是流量为N的最大费用流}

begin

GetMaxPath;

inc(Result,W[1,1]); {累计费用} i:=1;j:=1;

repeat {更新最大费用路上的流量}

case P[i,j] of 0: begin

inc(F[i,j,0]); inc(j);

end; 1: begin 3:

inc(F[i,j,1]); inc(i); end;

dec(j);

2: begin

dec(F[i,j,0]); end; begin dec(i); dec(F[i,j,1]); end;

end; until (i=Row) and (j=Col); end;

end;

begin assign(input,'input.txt');

assign(output,'output.txt'); ReadIn; Work;

rewrite(output); writeln(Result); close(output);

end.

第 29 页 共 29页

本文来源:https://www.bwwdw.com/article/hox3.html

Top