DP习题
动态规划入门练习题
http://drs.126.com 2003-12-4 大榕树
石子合并
在一个圆形操场的四周摆放着N堆石子(N<= 100),现要将石子有次序地合并成一堆.规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分.编一程序,由文件读入堆栈数N及每堆栈的石子数(<=20).
(!)选择一种合并石子的方案,使用权得做N-1次合并,得分的总和最小;
(2)选择一种合并石子的方案,使用权得做N-1次合并,得分的总和最小;
输入数据:
第一行为石子堆数N;
第二行为每堆的石子数,每两个数之间用一个空格分隔.
输出数据:
从第一至第N行为得分最小的合并方案.第N+1行是空行.从第N+2行到第2N+1行是得分最大合并方案.每种合并方案用N行表示,其中第i行(1<=i<=N)表示第i次合并前各堆的石子数(依顺时针次序输出,哪一堆先输出均可).要求将待合并的两堆石子数以相应的负数表示.
输入输出范例:
输入:
4
4 5 9 4
输出:
-4 5 9 -4
-8 -5 9
-13 -9
22 4 -5 -9 4
4 -14 -4
-4 -18
22
最小代价子母树 设有一排数,共n个,例如:22 14 7 13 26 15 11.任意2个相邻的数可以进行归并,归并的代价为该两个数的和,经过不断的归并,最后归为一堆,而全部归并代价的和称为总代价,给出一种归并算法,使总代价为最小.
输入、输出数据格式与“石子合并”相同。
输入样例:
4
12 5 16 4
输出样例:
-12 -5 16 4
17 -16 -4
-17 -20
37
背包问题 设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为XK,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于XK,而价值的和为最大。
输入数据:
第一行两个数:物品总数N,背包载重量XK;两个数用空格分隔;
第二行N个数,为N种物品重量;两个数用空格分隔;
第三行N个数,为N种物品价值; 两个数用空格分隔;
输出数据:
第一行总价值;
以下N行,每行两个数,分别为选取物品的编号及数量;
输入样例:
4 10
2 3 4 7
1 3 5 9
输出样例:
12
2 1
4 1
商店购物
某商店中每种商品都有一个价格。例如,一朵花的价格是2 ICU(ICU 是信息学竞赛的货币的单位);一个花瓶的价格是5 ICU。为了吸引更多的顾客,商店提供了特殊优惠价。特殊优惠商品是把一种或几种商品分成一组。并降价销售。例如:3朵花的价格不是6而是5 ICU ;2个花瓶加1朵花是10 ICU不是12 ICU。
编一个程序,计算某个顾客所购商品应付的费用。 要充分利用优惠价以使顾客付款最小。请注意,你不能变更顾客所购商品的种类及数量,
即使增加某些商品会使付款总数减小也不允许你作出任何变更。假定各种商品价格用优惠价如上所述, 并且某顾客购买物品为:3朵花和2个花瓶。那么顾客应付款为14 ICU因为:
1朵花加2个花瓶: 优惠价:10 ICU
2朵花 正常价: 4 ICU
输入数据
用两个文件表示输入数据。第一个文件INPUT.TXT描述顾客所购物品(放在购物筐中);第二个文件描述商店提供的优惠商品及价格(文件名为OFF ER.TXT)。 两个文件中都只用整数。
第一个文件INPUT.TXT的格式为:第一行是一个数字B(0≤B≤5),表示所购商品种类数。下面共B行,每行中含3个数C,K,P。 C 代表商品的编码(每种商品有一个唯一的编码),1≤C≤999。K代表该种商品购买总数,1≤K≤5。P 是该种商品的正常单价(每件商品的价格),1≤P≤999。请注意,购物筐中最多可放5*5=25件商品。
第二个文件OFFER.TXT的格式为:第一行是一个数字S(0≤S≤9 9),表示共有S 种优惠。下面共S行,每一行描述一种优惠商品的组合中商品的种类。下面接着是几个数字对(C,K),其中C代表商品编码,1≤C≤9 99。K代表该种商品在此组合中的数量,1≤K≤5。本行最后一个数字P(1≤
P≤9999)代表此商品组合的优惠价。当然, 优惠价要低于该组合中商品正常价之总和。
[b]输出数据[/b]
在输出文件OUTPUT.TXT中写 一个数字(占一行),
该数字表示顾客所购商品(输入文件指明所购商品)应付的最低货款。
[b]输入/输出数据举例 [/b]
[code]
INPUT OFFER.TXT
OUTPUT.TXT
-------------------------------------------------------------------
2 2
14
7 3 2 1 7 3 5
8 2 5 2 7 1 8 2 10
简析:
算法: 动态规划
数据结构: 字符串
题型: II 型
难度: 4 分
编程时间: 4分钟
简述: 本题竞赛时有一个很长的文件测试数据,用动态规划可较快的出答
案。
二、算法分析
1、数据结构
设当前购物筐中有商品组合(A,B,C,D,E)。其中
A──第1种商品数 (0≤A≤第1种商品的购卖总数);
B──第2种商品数 (0≤B≤第2种商品的购卖总数);
C──第3种商品数 (0≤C≤第3种商品的购卖总数);
D──第4种商品数 (0≤D≤第4种商品的购卖总数);
E──第5种商品数 (0≤E≤第5种商品的购卖总数);
当对筐中的商品组合(A,B,C,D,E)实行了某种优惠后,必须考虑该优惠范围外的各种商品数量。
设A、│K──实行了第k种优惠后的第1种商品的剩余量,即A-第K种优惠中第1 种商品的数量;
B、│K──实行了第k种优惠后的第2种商品的剩余量,即B-第K种优惠中第2种商品的数量;
C、│K──实行了第k种优惠后的第3种商品的剩余量,即C-第K种优惠中第3种商品的数量;
D、│K──实行了第k种优惠后的第4种商品的剩余量,即D-第K种优惠中第4种商品的数量;
E、│K──实行了第k种优惠后的第5种商品的剩余量,即E-第K种优惠中第5种商品的数量;
显然,对商品组合(A,B,C,D,E)实行第K种优惠的条件是A、│K≥0、B、│K≥0、C、│K≥0、D、│K≥0、E、│K≥0(1≤K≤优惠种数S)
下面,我们再来考虑如何表示购买商品组合(A,B,C,D,E)的最低货款?如何存贮实现最低价的最后一次优惠的序号?
设list(A,B,C,D,E)──购买商品组合(A,B,C,D,E)应付的最低贷款;
Ans(A,B,C,D,E)──最后选中的优惠序号:当最后选中了第Ans(A,B,C,D,E)种优惠后,才以最低价list(A,B,C,D,E)购入了筐内的商品;
0≤ List(A,B,C,D,E)≤A*商品1的单价+B*商品2的单价+C*商品3的单价+D*商品4的单价+E*商品5的单价;
1≤Ans(A,B,C,D,E)≤优惠种数S;
2.初始值的设定和规划方向
①设定初始值
最初时设定:
list(a,b,c,d,e)=a*商品1的单价+b*商品2的单价+c*商品3的单价+d*商品4的单价+ e*商品5的单价;
ans(a,b,c,d,e)=1;
0≤a≤第1种商品的购买总数;
0≤b≤第2种商品的购买总数;
0≤c≤第3种商品的购买总数;
0≤d≤第4种商品的购买总数;
0≤e≤第5种商品的购买总数;
表示筐中商品的所有组合最初都是以正常价购入, 且每一种商品组合的最低价都是从第1种优惠出发递推的。
②规划方向
采用顺推法。依次往购物筐中放入1个、2个…直至全部第5种商品;然后再依次往筐中放1个第4种商品,1个、2个…全部第5种商品;往筐中放2 个第4种商品, 1个、2个…全部第5种商品;…依次类推,直至5种商品全部放入为止。根据乘法原理,放置方案共有商品1的购买总数*商品2的购买总数*商品3的购买总数*商品4的购买总数*商品5的购买总数。
对于每一种放置的商品组合(a,b,c,d,e), 我们从第1种优惠出发,依次顺序考虑第1种优惠,第2种优惠,...第S种优惠, 直至求出购买商品组合(a,b,c,d,e)的最低价list(a,b,c,d, e)以及最后选中的优惠序号ans (a,b,c,d,e)。
显然可以采用6重for循环的程序结构。前5重for循环产生所有商品组合形式,第6重循环依次对当前筐内的商品组合考虑S种优惠。在循环体内对动态规划方程求解,得出购买筐内商品的最低价。
3. 动态规划方程
对于当前购物筐内的商品组合(a,b,c,d,e)依次考虑第1种优惠、第2种优惠、...、第S种优惠。
当考虑到第K(1≤K≤优惠种数S)种优惠时,若对筐内商品能实行第K种优惠(a、│K≥0、 b、│K≥0、 c、│K≥0、d、 │K≥0、e、 │K≥0)且对该优惠外的其余商品曾实行过某优惠或以正常价购入(K≥ans(a、│K, b、│K, c、│K,d、│K,e、│K)),
则可通过动态规划方程list(a,b,c,d,e)=min(list(a,b,c,d,e), list(a、│K, b、│K, c、│K,d、│K,e、 │K)+第K种商品组合优惠价);
ans(a,b,c,d,e)=K得出对商品组合(a,b,c,d,e)实行了第K种优惠后的当前最低价。每一种优惠依次考虑一遍。直到K=优惠总数S时, 便可求得购买该商品组合的最低价list(a,b,c,d,e)。
由此可见,从商品组合(0,0,0,0,1)出发,依次类推, 直至对商品组合(商品1的购买总数,商品2的购买总数,商品3的购买总数,商品4的购买总数,商品5的购买总数)考虑了第S种优惠后,list(商品1的购买总数, 商品2的购买总数,商品3的购买总数,商品4的购买总数, 商品5的购买总数)便是顾客所购商品应付的最低货款了。
由于在递推过程中,当前商品组合是在上一次商品组合的基础上增加一个商品后产生,当前商品组合的最优价也是在先前商品组合的最低价的基础上产生, 这个最低价的决策对过去各商品组合的最低价无甚影响,因此符合无后效性的性质。 采用动态规划求解显然是一种正确和高效的算法。
旅游预算
一个旅行社需要估算乘汽车从某城市到另一城市的最小费用,沿路有若干加油站,每个加油站收费不一定相同。旅游预算有如下规则:
若油箱的油过半,不停车加油,除非油箱中的油不可支持到下一站;每次加油时都加满;在一个加油站加油时,司机要花费2元买东西吃;司机不必为其他意外情况而准备额外的油;汽车开出时在起点加满油箱;计算精确到分(1元=100分)。编写程序估计实际行驶在某路线所需的最小费用。
输入格式:
从当前目录下的文本文件“route.in”读入数据。按以下格式输入若干旅行路线的情况:
第一行为起点到终点的距离(实数)
第二行为三个实数,后跟一个整数,每两个数据间用一个空格隔开。其中第一个数为汽车油箱的容量(升),第二个数是每升汽油行驶的公里数,第三个数是在起点加满油箱的费用,第四个数是加油站的数量。(〈=50〉。接下去的每行包括两个实数,每个数据之间用一个空格分隔,其中第一个数是该加油站离起点的距离,第二个数是该加油站每升汽油的价格(元/升)。加油站按它们与起点的距离升序排列。所有的输入都有一定有解。
输出格式:
答案输出到当前目录下的文本文件“route.out”中。
该文件包括两行。第一行为一个实数和一个整数,实数为旅行的最小费用,以元为单位,精确到分,整数表示途中加油的站的N。第二行是N个整数,表示N个加油的站的编号,按升序排列。数据间用一个空格分隔,此外没有多余的空格。
输入输出举例:
输入文件:(route.dat)
516.3
15.7 22.1 20.87 3
125.4 1.259
297.9 1.129
345.2 0.999
输出文件(route.out)
38.09 1
2
海上交通控制
海上交通图可以用一个有向图来表示,顶点表示港口,边表示两个港口之间是否有航线可通。为保证海上交通安全和以尽量快的速度到达目的地,每艘船在出发前都将航行计划(包括出发时间、速度、出发与到达港口)提交给海上交通控制局,由海上交通控制局为它们制定航线。现给出一系列的船只航行计划(包括出发时间、速度、出发与到达港口),请你根据以下原则编程为它们制定航线:
1、 每艘船在出发的一瞬间提交航行计划(提交和出发的时间差可以忽略);
2、 每艘船都严格按照出发时间出发,不能提前,也不能延迟;
3、 在任何时间一条航道(两港口间的直达航线)上只能有一艘船,因此,一艘船在出发的瞬间发现某航道将在末来的某段时间内会被在它之前出发的船占用,则它在那一段时间内将不会使用该航道,当然其余时间还是可以使用该航道;
4、 每个港口均可被无限艘船同时使用;
5、 在满足上述条件后,要使本船航行的时间最短;
6、 假如某船不能到达目标港口,那么它将放弃这个航程;
7、 船在任何时候都不能停下来,即从出发后,要一直航行到目的地,中途不得在航道或港口中停留。
时间用4位数字表示如2345表示23:45,速度单位用节(海里/小时)表示。在计算时间时,中间结果应是精确的时间(即不要四舍五入到分钟),而航行时间的计算是以总距离除以速度为准,最终到目标地的时刻应是航行时刻加上航行时间的四舍五入到分钟的结果。
输入格式:
从当前目录下的文本文件“LANE.in”读入数据。输入的数据一定有解,且不会出现跨越00:00的情况,例如,一艘船在23:55出发,第二天0:15到达的情况是不会出现的。输入文件开头是港口定义:
第一行是港口数N(〈=26〉;
第二行是一个长度为N的大写字母串,每个字母表示一个港口名字;
第三行开始N行的N X N矩阵是一个邻接矩阵,每行有N个整数,其值为港口间距离(单位为海里),整数间以空格分隔(若为0表示两港口没有直达航线相连);
接着的一行是一个整数M(〈=50〉,表示共有M艘船提交航行计划;接下去的每3行表示一艘船的航行计划,其中第一行是船名,第二行是出发时间和航速,两者均为整数,以一个空格分隔,第三行是两个大写安母,之间没有任何分隔,第一个表示出发的港口,第二个表示目的港口;
输出格式:
答案输出到当前目录下的文本文件“LANE.OUT”中。该文件的每3行表示一艘船的航线,其中第一行是船名,第二行是出发时间和到达时间,两者均为整数,以一个空格分隔,第三行是数个大写字母,之间没有任何分隔,表示该船经过的港口(包括出发和目的港口)。如果这艘船放弃航程时,到达时间用-1来表示,并留空第三行。
注意:在输入和输出中航行计划和航线均按出发时间排序,时间精确到分钟。
输入输出举例:
输入文件:LANE.in
5
ABCDE
0 10 0 50 10
10 0 20 70 0
0 20 0 20 0
50 70 20 0 10
10 0 0 10 0
4
Bluesky
0800 10
CB
Blackhorse
0900 5
AB
Greenforest
1000 20
DB
Silverboat
1200 20
DC
输出文件:LANE.OUT
Bluesky
800 1000
CB
Blackhorse
900 1100
AB
Greenforest
1000 1130
DEAB
Silverboat
1200 1300
DC
防卫导弹
一种新型的防卫导弹可截击多个攻击导弹。它可以向前飞行,也可以用很快的速度向下飞行,可以毫无损伤地截击进攻导弹,但不可以向后或向上飞行。但有一个缺点,尽管它发射时可以达到任意高度,但它只能截击比它上次截击导弹时所处高度低或者高度相同的导弹。现对这种新型
防卫导弹进行测试,在每一次测试中,发射一系列的测试导弹(这些导弹发射的间隔时间固定,飞行速度相同),该防卫导弹所能获得的信息包括各进攻导弹的高度,以及它们发射次序。现要求编一程序,求在每次测试中,该防卫导弹最多能截击的进攻导弹数量,一个导弹能被截击应满足下列两个条件之一:
1、 它是该次测试中第一个被防卫导弹截击的导弹;
2、 它是在上一次被截击导弹的发射后发射,且高度不大于上一次被截击导弹的高度的导弹。
输入格式:
从当前目录下的文本文件“CATCHER.DAT”读入数据。该文件的第一行是一个整数N(0〈=N〈=4000),表示本次测试中,发射的进攻导弹数,以下N行每行各有一个整数hi(0〈=hi〈=32767),表示第i个进攻导弹的高度。文件中各行的行首、行末无多余空格,输入文件中给出的导弹是按发射顺序排列的。
输出格式:
答案输出到当前目录下的文本文件“CATCHER.OUT”中,该文件第一行是一个整数max,表示最多能截击的进攻导弹数,以下的max行每行各有一个整数,表示各个被截击的进攻导弹的编号(按被截击的先后顺序排列)。输出的答案可能不唯一,只要输出其中任一解即可。
输入输出举例:
输入文件:CATCHER.DAT 输出文件:CATCHER.OUT
3 2
25 1
36 3
23
求函数最大值
已知3个函数A,B,C值如下表示,自变量取值为0----10的整数。请用动态规划的方法求出一组x,y,z,使得A(x)+B(y)+C(z)为最大,并且满足x*x+y*y+z*Z<N,N由键盘输入。
X 0 1 2 3 4 5 6 7 8 9 10
A(x) 2 4 7 11 13 15 18 22 18 15 11
B(x) 5 10 15 20 24 18 12 9 5 3 1
C(x) 8 12 17 22 19 16 14 11 9 7 4
Perform巡回演出 (GDKOI'2000)
题目描述:
Flute市的Phlharmoniker乐团2000年准备到Harp市做一次大型演出,本着普及古典音乐的目的,乐团指挥L.Y.M准备在到达Harp市之前先在周围一些小城市作一段时间的巡回演出,此后的几天里,音乐家们将每天搭乘一个航班从一个城市飞到另一个城市,最后才到达目的地Harp市(乐团可多次在同一城市演出).
由于航线的费用和班次每天都在变,城市和城市之间都有一份循环的航班表,每一时间,每一方向,航班表循环的周期都可能不同.现要求寻找一张花费费用最小的演出表.
输入:
输入文件包括若干个场景.每个场景的描述由一对整数n(2<=n<=10)和k(1<=k<=1000)开始,音乐家们要在这n个城市作巡回演出,城市用1..n标号,其中1是起点Flute市,n是终点Harp市,接下来有n*(n-1)份航班表,一份航班表一行,描述每对城市之间的航线和价格,第一组n-1份航班表对应从城市1到其他城市(2,3,...n)的航班,接下的n-1行是从城市2到其他城市(1,3,4...n)的航班,如此下去.
每份航班又一个整数d(1<=d<=30)开始,表示航班表循环的周期,接下来的d个非负整数表示1,2...d天对应的两个城市的航班的价格,价格为零表示那天两个城市之间没有航班.例如"3 75 0 80"表示第一天机票价格是75KOI,第二天没有航班,第三天的机票是80KOI,然后循环:第四天又是75KOI,第五天没有航班,如此循环.输入文件由n=k=0的场景结束.
输出:
对每个场景如果乐团可能从城市1出发,每天都要飞往另一个城市,最后(经过k天)抵达城市n,则输出这k个航班价格之和的最小值.如果不可能存在这样的巡回演出路线,输出0.
样例输入:
3 6
2 130 150
3 75 0 80
7 120 110 0 100 110 120 0
4 60 70 60 50
3 0 135 140
2 70 80
2 3
2 0 70
1 80
0 0
样例输出:
460
0
初看这道题,很容易便可以想到动态规划,因为第x天在第y个地方的最优值只与第x-1天有关,符合动态规划的无后效性原则,即只与上一个状态相关联,而某一天x航班价格不难求出S=C[(x-1) mod m +1].我们用天数和地点来规划用一个数组A[1..1000,1..10]来存储,A[i,j]表示第i天到达第j个城市的最优值,C[i,j,l]表示i城市与j城市间第l天航班价格,则A[i,j]=Min{A[i-1,l]+C[l,j,i] (l=1..n且C[l,j,i]<>0)},动态规划方程一出,尽可以放怀大笑了.
(五)、标号法
标号法是一种最佳算法,多用于求图的最短路问题。
一、标号法的概念:
所谓标号,是指与图的每一个顶点相对应的一个数字。标号法可以说是动态规划,它采用顺推的方法,对图的每一边检测一次,没有重复的回溯搜索,因此标号法是一种最佳算法。
二、标号法的算法流程:
现有一图G,求从起点Vs到终点Ve的最短距离。 设:
Sum(j)───顶点Vj的标号,代表的是Vs到Vj的最短距离。
Vj已标味着Vs到Vj的最短路以及这条路径的长度已求出。
M(i,j)───Vi到Vj的非负长度。
H(j)───顶点Vj的前趋结点。 标号法的算法流程如下:
sum(s)←0
↓
Vs进入队列L
↓
-----→移出队列L的队首Vk←-----
| ↓ |
| Vk是不是Ve------------------|---→计算结束打印路径
| N∣ Y |
| ↓ |
| 由Vk扩展出结点Vj |
| (Vk与Vj之间相连) |
| Sj←Sum(k)+M(k,j) |
| ↓ |
| Sj小于Sum(j) |
| | |
| Y | N |
| | --------------------
| |
| ↓
| Sum(j)←Sj
| H(j)← Vk
| Vj加入队列L并对队列L按Sum值由小到大排序
| ↓
---------------
注意:1.只有两个顶点间的距离为非负时,才可用标号法。 2.只有队列的首结点是目标结点时,才可停止计算。否则得出的不一定是最优解。
三、例题解析:
1.相邻项序列(GDOI97第四题)
问题描述:
对于一个N*N(<=100)的正整数矩阵M,存在从M[A1,B1] 开始到M[A2,B2]结束的相邻项序列.两个项M[I,J]和M[K,L]相邻的件是指满足如下情况之一:
(1)I=K+-1和J=L
(2)I=K和J=L+-1。
任务:从文件中输入矩阵M,再读入K(K<=4)组M[A1,B1]和M[A2,B2]的值。对于每一组M[A1,B1]和M[A2,B2],求一相邻项序列,使得相邻项之差的绝对值之和为最小。
输入格式:
4 ───N
1 9 6 12 ───每行N个数据,共N行
8 7 3 5
5 9 11 11
7 3 2 6
2 ───K
4 1 1 4 ───表示A1,B1和A2,B2的值,共K行 2 2 3 4
输出格式:
1 17 ───第一组数据相邻项之差的绝对值之和的最小值是17
7 5 8 7 9 6 12───第一组数据的相邻项序列
2 4
7 9 11 11
解析:本题若将相邻的两个数看作是两个顶点,两个数之差的绝对值作为权,则问题转化成求两个顶点的最短路问题。 设:Sum[I,J]为从起点Vs到结点M[I,J]的最短距离。 H[I,J]记录结点M[I,J]的前趋结点。 L为记录待扩展的结点的队列。 鉴于数组进行排序时速度较慢,所以用链表作为记录结点的队列的类型,适于排序。
四、小结
综上所述,标号法是动态规划的一种,它采用顺推的方法,对图的每一边检测一次,没有重复的回溯搜索,要比一般的搜索优秀得多。它是一种最佳算法。