-- 作者:gdgzgq
-- 发布时间:11/15/2004 9:31:52 PM
-- 动态规划
动态规划
一、动态规划的基本思想: 动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。
二、设计动态规划法的步骤: 1、找出最优解的性质,并刻画其结构特征; 2、递归地定义最优值(写出动态规划方程); 3、以自底向上的方式计算出最优值; 4、根据计算最优值时得到的信息,构造一个最优解。 步骤1-3是动态规划算法的基本步骤。在只需要求出最优值的情形,步骤4可以省略,步骤3中记录的信息也较少;若需要求出问题的一个最优解,则必须执行步骤4,步骤3中记录的信息必须足够多以便构造最优解。
三、动态规划问题的特征: 动态规划算法的有效性依赖于问题本身所具有的两个重要性质:最优子结构性质和子问题重叠性质。 1、最优子结构:当问题的最优解包含了其子问题的最优解时,称该问题具有最优子结构性质。 2、重叠子问题:在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,在以后尽可能多地利用这些子问题的解。
四、习题: 1、防卫导弹: 问题描述:一种新型的防卫导弹可截击多个攻击导弹。它可以向前飞行,也可以用很快的速度向下飞行,可以毫无损伤地截击进攻导弹,但不可以向后或向上飞行。但有一个缺点,尽管它发射时可以达到任意高度,但它只能截击比它上次截击导弹时所处高度低或者高度相同的导弹。现对这种新型 防卫导弹进行测试,在每一次测试中,发射一系列的测试导弹(这些导弹发射的间隔时间固定,飞行速度相同),该防卫导弹所能获得的信息包括各进攻导弹的高度,以及它们发射次序。现要求编一程序,求在每次测试中,该防卫导弹最多能截击的进攻导弹数量,一个导弹能被截击应满足下列两个条件之一: 1、它是该次测试中第一个被防卫导弹截击的导弹; 2、它是在上一次被截击导弹的发射后发射,且高度不大于上一次被截击导弹的高度的导弹。 输入格式:从当前目录下的文本文件"CATCHER.DAT"读入数据 。该文 件的第一行是一个整数N(0〈=N〈=4000),表示本次测试中,发射的进攻导弹数,以下N行每行各有一个整数hi(0〈=hi〈=32767),表示第i个进攻导弹的高度。文件中各行的行首、行末无多余空格,输入文件中给出的导弹是按发射顺序排列的。 输出格式:答案输出到当前目录下的文本文件"CATCHER.OUT"中,该文件第一行是一个整数max,表示最多能截击的进攻导弹数,以下的max行每行各有一个整数,表示各个被截击的进攻导弹的编号(按被截击的先后顺序排列)。输出的答案可能不唯一,只要输出其中任一解即可。 输入输出举例:
输入文件:CATCHER.DAT 3 25 36 23 输出文件:CATCHER.OUT 2 1 3
2、轮船(Ships) 描述 有一个国家被一条何划分为南北两部分,在南岸和北岸总共有N个城镇,每一城镇在对岸都有唯一的友好城镇。任何两个城镇都没有相同的友好城镇。每一对友好城镇都希望有一条航线来往。于是他们向政府提出了申请。由于河终年有雾。政府决定不允许有任两条航线交叉(如果两条航线交叉,将有很大机会撞船)。 你的任务是缟写一个程序来帮政府官员决定他们应拨款兴建哪些航线以使到没有出现交叉的航线最多。 输入数据 输入文件(ship.in)包括了若干组数据,每组数据格式如下: 第一行两个由空格分隔的整数x,y,10〈=x〈=6000,10〈=y〈=100。x 表示河的长度而y表示宽。第二行是一个整数N(1<=N<=5000),表示分布在河两岸的城镇对数。接下来的N行每行有两个由空格分隔的正数C,D(C、D〈=x〉,描述每一对友好城镇沿着河岸与西边境线的距离,C表示北岸城镇的距离而D表示南岸城镇的距离。在河的同一边,任何两个城镇的位置都是不同的。整个输入文件以由空格分隔的两个0结束。 输出数据 输出文件(ship.ou)要在连续的若干行里给出每一组数据在安全条件下能够开通的最大航线数目。 示例
Ship.in
30 4 7 22 4 2 6 10 3 15 12 9 8 17 17 4 2 0 0
Ship.out
4
?
?
? 动态规划(Dynamic Programming)是目前求解最优化等问题的常用算法 数塔问题:有形如图1.3-8所示的数塔,从顶部出发,在每一结点可以选择向左走或是向右走,一起走到底层,要 求找出一条路径,使路径上的值最大。
这道题如果用枚举法,在数塔层数稍大的情况下(如40),则需要列举出的路径条数将是一个非常庞大的数目。 如果用贪心法又往往得不到最优解。
在用动态规划考虑数塔问题时可以自顶向下的分析,自底向上的计算。从顶点出发时到底向左走还是向右走应取决 于是从左走能取到最大值还是从右走能取到最大值,只要左右两道路径上的最大值求出来了才能作出决策。同样的 道理下一层的走向又要取决于再下一层上的最大值是否已经求出才能决策。这样一层一层推下去,直到倒数第二层 时就非常明了。如数字2,只要选择它下面较大值的结点19前进就可以了。所以实际求解时,可从底层开始,层层 递进,最后得到最大值。 实际求解时应掌握其编程的一般规律,通常需要哪几个关键数组来存储变化过程这一点非常重要。 数塔问题的样例程序如下:
var a:array[1..50,1..50,1..3] of longint;i,j,n:integer; begin write( \'please input the number of rows:\'); readln(n); for i:=1 to n do for j:=1 to i do begin read(a[i,j,1]); a[i,j,2]:=a[i,j,1]; a[i,j,3]:=0 end; for i:=n-1 downto 1 do for j:=1 to i do if a[i+1,j,2]>a[i+1,j+1,2] then begin a[i,j,2]:=a[i,j,2]+a[i+1,j,2];a[i,j,3]:=0 end else begin a[i,j,2]:=a[i,j,2]+a[i+1,j+1,2];a[i,j,3]:=1 end; writeln(\'max=\',a[1,1,2]); j:=1; for i:=1 to n-1 do begin write(a[i,j,1],\'->\'); j:=j+a[i,j,3] end; writeln(a[n,j,1]) end.
|