动态规划初步.docx
《动态规划初步.docx》由会员分享,可在线阅读,更多相关《动态规划初步.docx(14页珍藏版)》请在冰豆网上搜索。
动态规划初步
动态规划初步
————————————————————————————————作者:
————————————————————————————————日期:
ﻩ
第八讲动态规划初步
一、动态规划简介
动态规划是运筹学的一个分支。
它是解决多阶段决策过程最优化问题的一种方法。
1951年,美国数学家贝尔曼(R.Bellman)提出了解决这类问题的“最优化原则”,1957年发表了他的名著《动态规划》,该书是动态规划方面的第一本著作。
动态规划问世以来,在工农业生产、经济、军事、工程技术等许多方面都得到了广泛的应用,取得了显著的效果。
动态规划运用于信息学竞赛是在90年代初期,它以独特的优点获得了出题者的青睐。
此后,它就成为了信息学竞赛中必不可少的一个重要方法,几乎在所有的国内和国际信息学竞赛中,都至少有一道动态规划的题目。
所以,掌握好动态规划,是非常重要的。
动态规划是一种方法,是考虑问题的一种途径,而不是一种算法。
因此,它不像深度优先和广度优先那样可以提供一套模式。
它必须对具体问题进行具体分析。
需要丰富的想象力和创造力去建立模型求解。
[例8-1]拦截导弹
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。
但是这种导弹拦截系统有一个缺陷:
虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。
某天,雷达捕捉到敌国的导弹来袭。
由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
样例:
INPUT OUTPUT
389207 155300299 170 15865 6(最多能拦截的导弹数)
ﻩﻩﻩﻩ 2(要拦截所有导弹最少要配备的系统数)
[问题分析]
第一部分是要求输入数据串中的一个最长不上升序列的长度,可使用递推的方法,具体做法是从序列的第一个元素开始,依次求出以第i个元素为最后一个元素时的最长不上升序列的长度len(i),递推公式为len
(1)=1,len(i)=max(len(j)+1),其中i>1,j=1,2,…i-1,且j同时要满足条件:
序列中第j个元素大于等于第i个元素。
第二部分比较有意思。
由于它紧接着第一问,所以很容易受前面的影响,采取多次求最长不上升序列的办法,然后得出总次数,其实这是不对的。
要举反例并不难,比如长为7的高度序列“754163 2”,最长不上升序列为“75432”,用多次求最长不上升序列的结果为3套系统;但其实只要2套,分别击落“7541”与“63 2”。
那么,正确的做法又是什么呢?
我们的目标是用最少的系统击落所有导弹,至于系统之间怎么分配导弹数目则无关紧要;上面错误的想法正是承袭了“一套系统尽量多拦截导弹”的思维定势,忽视了最优解中各个系统拦截数较为平均的情况,本质上是一种贪心算法。
如果从每套系统拦截的导弹方面来想行不通的话,我们就应该换一个思路,从拦截某个导弹所选的系统入手。
题目告诉我们,已有系统目前的瞄准高度必须不低于来犯导弹高度,所以,当已有的系统均无法拦截该导弹时,就不得不启用新系统。
如果已有系统中有一个能拦截该导弹,我们是应该继续使用它,还是另起炉灶呢?
事实是:
无论用哪套系统,只要拦截了这枚导弹,那么系统的瞄准高度就等于导弹高度,这一点对旧的或新的系统都适用。
而新系统能拦截的导弹高度最高,即新系统的性能优于任意一套已使用的系统。
既然如此,我们当然应该选择已有的系统。
如果已有系统中有多个可以拦截该导弹,究竟选哪一个呢?
当前瞄准高度较高的系统的“潜力”较大,而瞄准高度较低的系统则不同,它能打下的导弹别的系统也能打下,它够不到的导弹却未必是别的系统所够不到的。
所以,当有多个系统供选择时,要选瞄准高度最低的使用,当然瞄准高度同时也要大于等于来犯导弹高度。
解题时,用一个数组记下已有系统的当前瞄准高度,数据个数就是系统数目。
[程序清单]
constmax=1000;
vari,j,current,maxlong,minheight,select,tail,total:
longint;
height,longest,sys:
array [1..max]oflongint;
line:
string;
begin
write('Inputtestdata:
');
readln(line);
i:
=1;
whilei<=length(line) do
begin
while(i<=length(line)) and(line[i]='')doi:
=i+1;
current:
=0;
while(i<=length(line)) and(line[i]<>'')do
begin
current:
=current*10+ord(line[i])-ord('0');
i:
=i+1
end;
total:
=total+1;
height[total]:
=current
end;
longest[1]:
=1;
fori:
=2tototaldo
begin
maxlong:
=1;
forj:
=1 toi-1do
begin
ifheight[i]<=height[j]
theniflongest[j]+1>maxlong
thenmaxlong:
=longest[j]+1;
longest[i]:
=maxlong
end;
end;
maxlong:
=longest[1];
fori:
=2tototaldo
iflongest[i]>maxlongthenmaxlong:
=longest[i];
writeln(maxlong);
sys[1]:
=height[1]; tail:
=1;
fori:
=2 tototaldo
begin
minheight:
=maxint;
forj:
=1 to taildo
if sys[j]>height[i]then
ifsys[j] beginminheight:
=sys[j];select:
=jend;
ifminheight=maxint
thenbegin tail:
=tail+1;sys[tail]:
=height[i]end
elsesys[select]:
=height[i]
end;
writeln(tail)
end.
二、动态规划的几个基本概念
想要掌握好动态规划,首先要明白几个概念:
阶段、状态、决策、策略、指标函数。
1.阶段:
把所给问题的过程,恰当地分为若干个相互联系的阶段,以便能按一定的次序去求解。
描述阶段的变量称为阶段变量。
2.状态:
状态表示每个阶段开始所处的自然状况和客观条件,它描述了研究问题过程中的状况,又称不可控因素。
3.决策:
决策表示当过程处于某一阶段的某个状态时,可以作出不同的决定(或选择),从而确定下一阶段的状态,这种决定称为决策,在最优控制中也称为控制。
描述决策的变量,称为决策变量。
4.策略:
由所有阶段的决策组成的决策函数序列称为全过程策略,简称策略。
5.状态转移方程:
状态转移方程是确定过程由一个状态到另一个状态的演变过程。
6.指标函数:
用来衡量所实现过程优劣的一种数量指标,称为指标函数。
指标函数的最优值,称为最优值函数。
三、确定动态规划的思路
1、采用动态规划来解决问题,必须符合两个重要的条件。
(1)“过去的历史只能通过当前状态去影响它未来的发展,当前的状态是对以往历史的一个总结”,这种特性称为无后效性,是多阶段决策最优化问题的特征。
(2)作为整个过程的最优策略具有这样的性质:
即无论过去的状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。
简言之,一个最优策略的子策略总是最优的。
这就是最优化原理。
2、如果碰到一个问题,能够满足以上两个条件的话,那么就可以去进一步考虑如何去设计使用动态规划:
(1)划分阶段。
把一个问题划分成为许多阶段来思考
(2)设计合适的状态变量(用以递推的角度)
(3)建立状态转移方程(递推公式)
(4)寻找边界条件(已知的起始条件)
如果以上几个步骤都成功完成的话,我们就可以进行编程了。
四、动态规划解题的一些技巧
ﻩ由于动态规划并没有一个定式,这就需要去开拓我们创造力去构造并且使用它。
以下,通过一些具体的竞赛实例谈谈使用动态规划过程中的一些技巧。
[例8-2]堆塔问题,设有n个边长为1的正立方体,在一个宽为1的轨道上堆塔,但塔本身不能分离。
例如n=1时,只有1种方案□
n=2时 有2种方案
堆塔的规则为底层必须有支撑,如下列的堆法是合法的.
下面的堆法是不合法的.
程序要求:
输入n(n<=40),求出
①总共有多少种不同的方案
②堆成每层的方案数是多少,例如n=6时,堆成1层的方案数为1,……堆成6层的方案数为1……
[问题分析]问题①的分析见第七讲例7-3,关于问题②,可以这样考虑,将n个立方体的第一列去掉的话,则成为一个比n小的堆塔问题,这样问题②就可以用动态规划法来解。
具体方法是从1到n依次求出堆成每层的方案数,设f(n,k)为堆成k层的方案数,则递推公式为:
[算法设计] 由于n最大可达到40,所以堆塔的总方案数超过了长整形数的范围,程序采用了高精度加法运算。
[程序清单]
program ex8_2(input,output);
constmaxn=40;maxlen=maxndiv3;
typearraytype=array[0..maxlen]ofinteger;
var i,j,k,n,nn:
longint;
total:
arraytype;
f:
array[0..maxn,0..maxn] ofarraytype;
procedure add(vara:
arraytype;b:
arraytype);
vari:
longint;
begin
for i:
=0 tomaxlendoa[i]:
=a[i]+b[i];
fori:
=0 to maxlen-1do
ifa[i]>=10then
begin
a[i+1]:
=a[i+1]+1;
a[i]:
=a[i]-10
end
end;
procedureprint(a:
arraytype);
vari,j:
longint;
begin
i:
=maxlen;
while (i>0)and(a[i]=0) do i:
=i-1;
forj:
=idownto0dowrite(a[j])
end;
begin
write('Input n:
');
readln(n);
fori:
=1tomaxlendototal[i]:
=0;
total[0]:
=1;
for i:
=1 ton-1doadd(total,total);
for i:
=1 tomaxn do
forj:
=1 tomaxn do
for k:
=0tomaxlen do f[i,j,k]:
=0;
fori:
=1 tomaxndof[i,1,0]:
=1;
fori:
=1to maxndof[i,i,0]:
=1;
fornn:
=2to ndo
fork:
=2to nn-1do
begin
fori:
=1to k-1doadd(f[nn,k],f[nn-i,k]);
fori:
=1tok do
ifnn-k>=ithenadd(f[nn,k],f[nn-k,i])
end;
write('Total=');
print(total);
writeln;
fork:
=1tondo
begin
write('Height=',k:
2,'Kind=':
10);
print(f[n,k]);
writeln;
if kmod 23=0
then
begin
write('Press<Enter>tocontinue!
');
readln
end
end;
write('Presstocontinue!
');
readln
end.
[例8-3]投资问题:
有n万元的资金,可投资于m个项目,其中m 和n为小于100的自然数。
对第i(1≤i≤m)个项目投资j万元(1≤j≤n,且 j为整数)可获得的回报为Q(i,j),请编程序,求解并输出最佳的投资方案(即获得回报总值最高的投资方案)。
输入数据放在一个文本文件中,格式如下:
m n
Q(1 ,0)Q(1,1)······Q(1 ,n)
Q(2 , 0)Q(2 ,1)······Q(2 , n)
············
Q(m,0)Q(m,1)······Q(m,n)
输出数据格式为:
r
(1)r
(2)······r(m)P
其中r(i)(1≤i≤m)表示对第i个项目的投资万元数,P为总的投资回报值,保留两位有效数字,任意两个数之间空一格。
当存在多个并列的最佳投资方案时,只要求输出其中之一即可。
如输入数据如下时:
2 3
01.11.3 1.9
02.12.5 2.6
屏幕应输出:
123.6
[问题分析]本题可供考虑的递推角度只有资金和项目两个角度,从项目的角度出发,逐个项目地增加可以看出只要算出了对前k个项目投资j万元最大投资回报值(1≤j≤n),就能推出增加第k+1个项目后对前k+1个项目投资j万元最大投资回报值(1≤j≤n),设P[k,j]为前k个项目投资j万元最大投资回报值,则P[k+1,j]=Max(P[k,i]+Q[k+1,j-i]),0<=i<=j,k=1时,对第一个项目投资j万元最大投资回报值(0≤j≤n)是已知的(边界条件)。
[算法设计]动态规划的时间复杂度相对于搜索算法是大大降低了,却使空间消耗增加了很多。
所以在设计动态规划的时候,需要尽可能节省空间的占用。
本题中如果用二维数组存储原始数据和最大投资回报值的话,将造成空间不够,事实上,从前面的问题分析可知:
在计算对前k+1个项目投资j万元最大投资回报值时,只要用到矩阵Q的第k+1行数据和对前k个项目投资j万元最大投资回报值,而与前面的数据无关,后者也只需有一个一维数组存储即可,程序中用一维数组Q存储当前项目的投资回报值,用一维数组maxreturn存储对当前项目之前的所有项目投资j万元最大投资回报值(0≤j≤n),用一维数组temp存储对到当前项目为止的所有项目投资j万元最大投资回报值(0≤j≤n)。
为了输出投资方案,程序中使用了一个二维数组invest,invest[k,j]记录了对前k个项目投资j万元获得最大投资回报时投资在第k个项目上的资金数。
[程序清单]
programex8_3(input,output);
constmaxm=100;maxn=100;
typearraytype=array [0..maxn]ofreal;
vari,j,k,m,n,rest:
integer;
q,maxreturn,temp:
arraytype;
invest:
array[1..maxm,0..maxn]ofinteger;
result:
array[1..maxm] of integer;
fname:
string;
f:
text;
begin
write('Inputthenameofdatafile:
');
readln(fname);
assign(f,fname);
reset(f);
readln(f,m,n);
forj:
=0 tondoread(f,q[j]);
readln(f);
fori:
=1 tom do
forj:
=0tondoinvest[i,j]:
=0;
maxreturn:
=q;
forj:
=0tondoinvest[1,j]:
=j;
fork:
=2to m do
begin
temp:
=maxreturn;
for j:
=0tondoinvest[k,j]:
=0;
forj:
=0to ndoread(f,q[j]);
readln(f);
forj:
=0to ndo
fori:
=0toj do
if maxreturn[j-i]+q[i]>temp[j] then
begin
temp[j]:
=maxreturn[j-i]+q[i];
invest[k,j]:
=i
end;
maxreturn:
=temp
end;
close(f);
rest:
=n;
fori:
=mdownto1do
begin
result[i]:
=invest[i,rest];
rest:
=rest-result[i]
end;
for i:
=1tom do write(result[i],' ');
writeln(maxreturn[n]:
0:
2)
end.
[例8-4] 花店橱窗布置问题(FLOWER)
(1)问题描述
假设你想以最美观的方式布置花店的橱窗。
现在你有F束不同品种的花束,同时你也有至少同样数量的花瓶被按顺序摆成一行。
这些花瓶的位置固定于架子上,并从1至V顺序编号,V是花瓶的数目,从左至右排列,则最左边的是花瓶1,最右边的是花瓶V。
花束可以移动,并且每束花用1至F间的整数唯一标识。
标识花束的整数决定了花束在花瓶中的顺序,如果I<J,则令花束I必须放在花束J左边的花瓶中。
例如,假设一束杜鹃花的标识数为1,一束秋海棠的标识数为2,一束康乃馨的标识数为3,所有的花束在放入花瓶时必须保持其标识数的顺序,即:
杜鹃花必须放在秋海棠左边的花瓶中,秋海棠必须放在康乃馨左边的花瓶中。
如果花瓶的数目大于花束的数目。
则多余的花瓶必须空置,且每个花瓶中只能放一束花。
每一个花瓶都具有各自的特点。
因此,当各个花瓶中放入不同的花束时,会产生不同的美学效果,并以美学值(一个整数)来表示,空置花瓶的美学值为零。