边界条件f0(s0)=0(0≤s0≤V)
两种划分阶段的方法,引出了两种状态表示法,两种规划方式,但是却都成功地解决了问题。
只不过因为决策的选择有多有少,所以算法的时间复杂度也就不同。
这个例子具有很大的普遍性。
有很多的多阶段决策问题都有着不止一种的阶段划分方法,因而往往就有不止一种的规划方法。
有时各种方法所产生的效果是差不多的,但更多的时候,就像上述例子一样,两种方法会在某个方面有些区别。
(2)动态规划的模式性
动态规划的设计有着一定的模式,一般都要经历以下几个步骤。
划分阶段:
按照问题的时间或空间特征,把问题分为若干个阶段。
注意这若干个阶段一定要是有序的或者是可排序的,否则问题就无法求解。
选择状态:
将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。
当然,状态的选择要满足无后效性。
确定决策并写出状态转移方程:
之所以把这两步放在一起,是因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。
所以,如果确定了决策,状态转移方程也就写出来了。
但事实上,常常是反过来做,根据相邻两段的各状态之间的关系来确定决策。
写出规划方程(包括边界条件):
一般说来,只要阶段、状态、决策和状态转移确定了,要写出规划方程还是比较简单的。
图2.1动态规划实现的模式框架N-S图
掌握了动态规划的模式性,在用动态规划考虑问题时就可以把主要的精力放在理
论的设计上面。
一旦设计成熟,问题也就基本上解决了,而且在设计算法时也可以按部就班地进行。
(3)动态规划的技巧性
上面所说的动态规划的模式性,主要指的是实现方面。
而在设计方面,虽然它有较为严格的步骤性,但是它的设计思想却是没有一定的规律可循的。
这就需要在实践当中不断地去掌握动态规划的技巧,下面仅就一个例子谈一谈技巧性。
图2.2街道问题图2.3街道问题虚线划分阶段图2.4街道问题重新划分阶段
街道问题:
在图2.2中找出从左下角到右上角的最短路径,每步只能向右方或上方走。
这是一个简单而又典型的动态规划问题,许多介绍动态规划的书与文章中都拿它来做例子。
通常,书上的解答是这样的:
按照图2.3中的虚线来划分阶段,即阶段变量k表示走过的步数,而状态变量Sk表示当前处于这一阶段上的哪一点(各点所对应的阶段和状态已经用ks在地图上标明)。
这时的模型实际上己经转化成了一个特殊的多段图。
用决策变量uk=0表示向右走,uk=1表示向上走,则状态转移方程如下:
Sk–1+uk(K≤row)
Sk-1=
Sk+uk(K>row)
(这里的row是地图竖直方向的行数)
可以看到,这个状态转移方程需要根据k的取值分两种情况讨论,显得非常麻烦。
相应的,把它代入规划方程而付诸实现时,算法也很繁。
因而在实现时,一般是不会这么做的,而代之以下面方法:
将地图中的点规则地编号如图2.4,得到的规划方程如下:
fi-1,j+Distance(i-1,j)(i,j)
fi,j=Min
fi-1,j+Distance(i-1,j)(i,j)
(这里Distance表示相邻两点间的边长)
这样做确实要比上面的方法简单多了,但是它已经破坏了动态规划的本来面目,而不存在明确的阶段特征了。
如果说这种方法是以地图中的行(A、B、C、D)来划分阶段的话,那么它“状态转移”就不全是在两个阶段之间进行的了。
也许这没什么大不了的,因为实践比理论更有说服力。
但是,如果把问题扩展一下:
在地图中找出从左下角到右上角的两条路径,两条路径中的任何一条边都不能重叠,并且要求两条路径的总长度最短。
这时,再用这种“简单”的方法就很难实现了。
如果一定要套用这种方法的话,则最优指标函数就需要有四维的下标,并且难以处理两条路径“不能重叠”的问题。
而回到原先“标准”的动态规划法,就会发现这个问题很好解决,只需要加一维状态变量就可以实现了。
即用sk=(ak,bk)分别表示两条路径走到阶段k时所处的位置,相应的,决策变量也增加一维,用uk=(xk,yt)分别表示两条路径的行走方向。
状态转移时将两条路径分别考虑:
ak-1=a-1+xk
(K≤row)
bk-1=b-1+yk
ak-1=a+xk
(K>row)
bk-1=b+yk
在写规划方程时,只要对两条路径走到同一个点的情况稍微处理一下,减少可选的决策个数:
min(fk-1(TK(Sk,Uk))+两条边长)ak=bk
Uk=(0,1),(1,0)
fk(sk)=
min(fk-1(TK(Sk,Uk))+两条边长)ak≠bk
Uk=(0,1),(1,0)
从这个例子中可以总结出设计动态规划算法的一个技巧:
状态转移一般是在相邻的两个阶段之间(有时也可以在不相邻的两个阶段间),但是尽量不要在同一个阶段内进行。
动态规划是一种很灵活的算法设计方法,在动态规划算法的设计中,类似的技巧还有很多。
要掌握动态规划的技巧,有两条途径:
一是要深刻理解动态规划的本质,这也是为什么一开始就探讨它的本质的原因;二是要多实践,不但要多应用,还要学会从应用中探寻规律,总结技巧。
4动态规划算法在具体问题中的应用
所选的例子产生的数据量不是太大,简单动态规划能够解决。
(1)问题描述
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。
在桥上有一些石子,青蛙很讨厌踩在这些石子上。
由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:
0,1,……,L(其中L是桥的长度)。
坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。
青蛙从桥的起点开始,不停的向终点方向跳跃。
一次跳跃的距离是S到T之间的任意正整数(包括S,T)。
当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
事先给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。
你的任务是确定青蛙要想过河,最少需要踩到的石子数。
输入文件:
输入文件river.in的第一行有一个正整数L(1≤L≤109),表示独木桥的长度。
第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1≤S≤T≤10,1≤M≤100。
第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。
所有相邻的整数之间用一个空格隔开。
输出文件:
输出文件river.out只包括一个整数,表示青蛙过河最少需要踩到的石子数。
样例输入:
10
235
23567
样例输出:
2
数据规模:
对于30%的数据,L≤10000;
对于全部的数据,L≤109。
时限:
2秒。
(2)样例分析
桥长度L=10,最短能跳S=2,最长能跳T=3,石子数M=5,5个石子所在位置分别为2、3、5、6、7。
跳过桥最少要踩到的石子数是2个,如图2.1所示:
图3.1“过河”问题样例分析
(3)算法分析
1、这个问题初看是一个典型的搜索问题,从河的一侧到河的另一侧,要找最少踩到的石头数。
只要时间和空间足够,利用递归穷举每一种跳过桥的方法,那是完全可以实现的,也是最容易想到的,有人就是这么做的,还通过了一个数据量比较小的测试数据。
但从最大数据范围来看,1..109长度的桥,就算是O(n)的算法也不能在一秒内出解。
2、当然,最特殊的情况应该是S和T相等,即每次只能以相同长度进行跳跃,则只要统计相应倍数上的石子数即可。
3、如果使用以石子分阶段的一维动态规划,时间复杂度O(L)。
最多也只有100、100的时间。
但是这样分状态就十分复杂。
因为石头的分布是没有任何规律的,而且会有后效性。
4、只有以桥上的每个相等距离间隔的整数点作为动态规划的对象,时间复杂度为O(L),从最优子结构和无后效性的两个方面来看整个问题:
到达每一个点的最少石子数可由距离当前点之前长度为S和T间的一些点确定(刚开始的几个点可以通过临时改变S和T的值来处理),而且一旦该值确定,则不会受后续点取值的影响;另外,距离当前点之前长度为S和T间的一些点的最终值的确定又可以沿用以上方法,这就从理论上满足了最优子结构和无后效性的要求,可以使用这种动态规划实现。
可以总结出以下动态转移方程(记:
A[i]表示i点上的石子数目,P[i]表示到达i点的最少石子数):
∞;(i
min{P[i]}+A[i];(iP[i]=0≤j≤s
min{P[i]}+A[i];(i≥T)
i-T≤j≤i-s
样例中的数据按照动态规划方法实现,得到的P数组序列如图2.2所示,初始点的值为0,因为1号点位置不能达到,所以到达该点所要踩到的最少石子数为二,依次计算,最后的结果应该从含终点在内的之后几个点的数值取最小值,最小值为2。
图3.2“过河”问题样例的动态规划实现过程
5、由于L可能达到109,从时间复杂度和空间复杂度来说还是不能满足要求,能不能减少不必要的操作呢。
再看,从桥的一侧到另一侧,中间最多只有100个石子。
假设桥长为最大值(109),石头数也为最大值(100)。
这样中间一定会有很多“空长条”(两个石子中的空地),处理时把这些跳过,就会减少很多次运算。
关键是找出每一个可以跳过的“空长条”。
其实可以断言:
两个相隔较远(距离大于100)的点可以移近一点,通过移动,则相应数据在时间和空间上都能接受了。
因为有石子的点,如果后续有很长的空长条,则到达这些空长条点的最少石子数逐渐趋于一个稳定的值。
青蛙跳1次,2次,3次……的距离范围分别是[s,T],[2S,2T],[3S,3T]……当[NS,NT]与[(N+1)S,(N+1)T]相接的时候,说明[NS,+∞]的距离青蛙都是可以跳到的。
假设两个相邻石子的坐标是x和Y(X当X和Y之间的距离很大时,无论A的值是多少,Y-1,Y-2,……Y-T的位置青蛙都可以跳到。
因此可以缩短X和Y之间的距离,只要保证Y-1,Y-2,……Y-T这T个位置,青蛙仍然都可以跳到就可以了(这是因为青蛙到达T及其以后的位置跳法只跟这T个位置有关,现在虽然距离减小了,但是青蛙的选择并没有减少,所以是等价的)。
又由于1≤S≤T≤10,将间距大于100的点移近至间距100是完全合理的,从而桥的总长度就缩小至I00x100=10000以内,最长运算次数也只有I0000x100,利用动态规划完全可以在规定的时间和空间内实现。
6、作为一个完整的程序,因为给出条件中石子位置并未排好序,由于最多100个位置,所以采用一般的排序方法都能在很短的时间内实现;最后解的确定应该由跳出桥后的一段距离上的点的取值确定。
(4)问题实现
读入所有初始数据并存储
对石子所在位置递增排序
以石子所在位置穷举(最多100次)
若相邻A、B两点间距超过100,则将B及后续所有点整体往前移动,目标要要求A、B间距调整为100
到达任意点的最少石子数初始化(∞),零点为0
以调整后的桥上的整数点(0~最后石子点的后续10个点的位置)穷举
以距离当前点之前长度为S和T间的一些点穷举(最初的几个点特色处理)
是
比较从该点调至当前点是否更优
否
计算新值替代原有值
不作操作
从最后石子点的后续10个点的位置上找出最小指输出
图3.3“过河”问题的动态规划实现N—S图
(5)测试结果
表3.1“过河”问题测试结果
测试点
正确性
所用时间
第1次测试
第2次测试
1
正确
0.03秒
0.03秒
2
正确
0.03秒
0.03秒
3
正确
0.03秒
0.03秒
4
正确
0.03秒
0.02秒
5
正确
0.03秒
0.03秒
6
正确
0.03秒
0.05秒
7
正确
0.03秒
0.05秒
8
正确
0.03秒
0.03秒
9
正确
0.03秒
0.03秒
10
正确
0.03秒
0.03秒
结论:
由于列举测试数据所占的篇幅较大,在文中就不给出了。
从表3.1的测试情况可以得知,利用动态规划设计相应算法,在较短时间之内就能得到相应的正确结果。
参考文献
[1]吴耀斌,曹利国,向期中.信息学奥林匹克教程.第1版.湖南:
湖南师范大学出版社,2001
[2]谢水英.例谈几种常见的动态规划模型.中小学电脑报NOI专刊.2007(8)
[3]鞠向忠,沙聚祯一般动态规划模型及其理论初探.黑龙江科技学院学报.1994
(2)
[4]冯小虎.动态规划思想在算法设计中的应用.安徽电子信息职业技术学院学报.2004
(2)
[5]金孚安一类动态规划问题的解法与算法.微机发展.2001(5)
[6]王刚.动态规划的应用实例.云南财贸学院学报(经济管理版).2001
(1)