动态规划大致分为线型动态规划和区间型动态规划。
本章我们先谈线型动态规划。
采药(选自NOIP2005普及组复赛第三题)
【难度指数】简单
【问题描述】
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”如果你是辰辰,你能完成这个任务吗?
【输入文件】
输入文件medic.in的第一行有两个整数T(1 <= T<= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
【输出文件】
输出文件medic.out包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
【样例输入】
70 3
71 100
69 1
1 2
【样例输出】
3
【数据规模】
对于30%的数据,M <= 10;对于全部的数据,M <= 100。
【思路分析】
f[i,j]表示j时间内采前i种草药能得到的最大价值,f[m,t]为本题答案。
var
time,value:array[1..100] of integer;
t,m:integer;
f:array[0..100,0..1000] of integer;
procedure init;
var
i:integer;
begin
assign(input,'medic.in');
reset(input);
readln(t,m);
for i:=1 to m do readln(time[i],value[i]);
close(input);
end;
procedure solve;
var
i,j:integer;
begin
for i:=0 to t do f[0][i]:=0;
for i:=1 to m do
begin
for j:=0 to t do
begin
if j<time[i] then
begin
f[i][j]:=f[i-1][j];
end
else
begin
if f[i-1][j-time[i]]+value[i]>f[i-1][j] then
begin
f[i][j]:=f[i-1][j-time[i]]+value[i];
end
else
begin
f[i][j]:=f[i-1][j];
end;
end;
end;
end;
end;
procedure answer;
begin
assign(output,'medic.out');
rewrite(output);
writeln(f[m][t]);
close(output);
end;
begin
init;
solve;
answer;
end.
开心的金明(选自NOIP2006普及组复赛第二题)
【难度指数】简单
【问题描述】
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1-5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:
v[j1]*w[j1]+v[j2]*w[j2]+…+v[jk]*w[jk]。(其中*为乘号)
请你帮助金明设计一个满足要求的购物单。
【输入文件】
输入文件happy.in 的第1行,为两个正整数,用一个空格隔开:N m(其中N(<30000)表示总钱数,m(<25)为希望购买物品的个数。)从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有2个非负整数v p(其中v表示该物品的价格(v<=10000),p表示该物品的重要度(1-5))
【输出文件】
输出文件happy.out只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)。
【输入样例】
1000 5
800 2
400 5
300 5
400 3
200 2
【输出样例】
3900
【思路分析】
f[i,j]表示用j元买前i种物品能得到的最大价值,f[m,n]为本题答案。
var
price:array[1..25] of longint;
grade:array[1..25] of integer;
f:array[0..25,0..30000] of longint;
m:integer;
n:longint;
procedure init;
var
i:integer;
begin
assign(input,'happy.in');
reset(input);
read(n,m);
for i:=1 to m do
begin
readln(price[i],grade[i]);
end;
close(input);
end;
procedure solve;
var
i,j:integer;
begin
for i:=0 to n do f[0,i]:=0;
for i:=1 to m do
begin
for j:=0 to n do
begin
if j<price[i] then f[i,j]:=f[i-1,j]
else
begin
if f[i-1][j-price[i]]+price[i]*grade[i]>f[i-1][j] then
begin
f[i][j]:=f[i-1][j-price[i]]+price[i]*grade[i];
end
else
begin
f[i][j]:=f[i-1][j];
end;
end;
end;
end;
end;
procedure answer;
begin
assign(output,'happy.out');
rewrite(output);
write(f[m][n]);
close(output);
end;
begin
init;
solve;
answer;
end.
拦截导弹(选自NOIP1999提高组复赛第一题)
【难度指数】中等
【问题描述】
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
【输入样例】
389 207 155 300 299 170 158 65
【输出样例】
6(最多能拦截的导弹数)
2(要拦截所有导弹最少要配备的系统数)
【思路分析】
a[i]表示如果第i枚导弹被拦则一共能拦截的导弹数,即第i枚导弹本身加上后续可拦截的导弹数。最后数组a中的最大值即为本题答案。
var
ans1,ans2,n,m:integer;
h,a,sys:array[1..50] of integer;
procedure init;
begin
assign(input,'missile.in');
reset(input);
while not eoln(input) do
begin
inc(n);
read(h[n]);
end;
end;
procedure solve;
var
i,j,max,min,select:integer;
find:boolean;
begin
//task1
a[n]:=1;
for i:=n-1 downto 1 do
begin
max:=0;
for j:=i+1 to n do
begin
if (h[j]<=h[i]) and (max<a[j]) then
begin
max:=a[j];
end;
end;
a[i]:=max+1;
if a[i]>ans1 then ans1:=a[i];
end;
//task2
m:=1;
sys[m]:=maxint;
for i:=1 to n do
begin
//choose a proper system
min:=maxint;
find:=false;
for j:=1 to m do
begin
if (sys[j]>=h[i]) and (sys[j]<=min) then
begin
min:=sys[j];
select:=j;
find:=true;
end;
end;
if find=false then
begin
inc(m);
sys[m]:=h[i];
end
else
begin
sys[select]:=h[i];
end;
end;
ans2:=m;
end;
procedure answer;
begin
assign(output,'missile.out');
rewrite(output);
writeln(ans1);
writeln(ans2);
end;
begin
init;
solve;
answer;
end.
堆集装箱(改编自USACO Jan07The Bale Tower)
【难度指数】中等
【问题描述】
有N个集装箱,3 <= N <= 20。每个集装箱的长各不相同,宽也各不相同。集装箱不允许旋转,即长和宽不能互换。现在要在这些集装箱中选择若干个堆叠起来。要求上面集装箱的长必须小于下面集装箱的长,上面集装箱的宽也必须小于下面集装箱的宽。问最多能选几个集装箱堆成一堆。
【输入文件】
输入文件btwr.in第一行是N。接下来的N行,每行两个正整数,分别表示集装箱长和宽。
【输出文件】
输出文件btwr.out一个整数,表示最多能选几个集装箱堆成一堆。
【样例输入】
6
6 9
10 12
9 11
8 10
7 8
5 3
【样例输出】
5
var
a,b,x:array[1..50] of integer;
n,ans:integer;
procedure init;
var
i:integer;
begin
assign(input,'btwr.in');
reset(input);
readln(n);
for i:=1 to n do
begin
readln(a[i],b[i]);
end;
end;
procedure debug;
var
i:integer;
begin
for i:=1 to n do
begin
writeln(a[i]:5,b[i]:5);
end;
for i:=1 to n do writeln(x[i]:5);
end;
procedure qsort(left,right:integer);
var
i,j,x,tempx,tempy:integer;
begin
i:=left;
j:=right;
x:=a[(left+right) div 2];
repeat
while a[i]<x do inc(i);
while a[j]>x do dec(j);
if i<=j then
begin
tempx:=a[i];
tempy:=b[i];
a[i]:=a[j];
b[i]:=b[j];
a[j]:=tempx;
b[j]:=tempy;
inc(i);
dec(j);
end;
until i>j;
if left<j then qsort(left,j);
if i<right then qsort(i,right);
end;
procedure solve;
var
i,j,max:integer;
begin
qsort(1,n);
x[1]:=1;
for i:=2 to n do
begin
max:=0;
for j:=i-1 downto 1 do
begin
if (a[i]>a[j]) and (b[i]>b[j]) and (x[j]>max) then
begin
max:=x[j];
end;
end;
x[i]:=max+1;
if x[i]>ans then ans:=x[i];
end;
end;
procedure answer;
begin
assign(output,'btwr.out');
rewrite(output);
writeln(ans);
close(output);
end;
begin
init;
solve;
answer;
end.
嵌套矩形(选自ACM)
【难度指数】困难
【问题描述】
有n个矩形,每个矩形可以用a,b来描述, 表示长和宽。矩形X(a,b)可以嵌套在矩形Y(c,d)中当且仅当a<c,b<d或者b<c,a<d(相当于旋转X90度)。 例如(1,5)可以嵌套在(6,2)内,但不能嵌套在(3,4)中。你的任务是选出尽可能多的矩形排成一行,使得除最后一个外,每一个矩形都可以嵌套在下一个矩形内。
【输入文件】
输入文件nest.in。
第1行n (n<=2000)
第2到n+1行每行两个数a,b,表示这个矩形的长和宽
【输出文件】
输出文件nest.out一个数,最多符合条件的矩形数目。
【样例输入】
3
1 5
6 2
3 4
【样例输出】
2
var
a,b,x:array[1..50] of integer;
n,ans:integer;
procedure init;
var
i:integer;
begin
assign(input,'nest.in');
reset(input);
readln(n);
for i:=1 to n do
begin
readln(a[i],b[i]);
end;
end;
procedure debug;
var
i:integer;
begin
for i:=1 to n do
begin
writeln(a[i]:5,b[i]:5);
end;
for i:=1 to n do writeln(x[i]:5);
end;
procedure qsort(left,right:integer);
var
i,j,x,tempx,tempy:integer;
begin
i:=left;
j:=right;
x:=a[(left+right) div 2];
repeat
while a[i]<x do inc(i);
while a[j]>x do dec(j);
if i<=j then
begin
tempx:=a[i];
tempy:=b[i];
a[i]:=a[j];
b[i]:=b[j];
a[j]:=tempx;
b[j]:=tempy;
inc(i);
dec(j);
end;
until i>j;
if left<j then qsort(left,j);
if i<right then qsort(i,right);
end;
procedure solve;
var
i,j,temp,max:integer;
begin
for i:=1 to n do
begin
if a[i]>b[i] then
begin
temp:=a[i];
a[i]:=b[i];
b[i]:=temp;
end;
end;
qsort(1,n);
x[1]:=1;
for i:=2 to n do
begin
max:=0;
for j:=i-1 downto 1 do
begin
if (a[i]>a[j]) and (b[i]>b[j]) and (x[j]>max) then
begin
max:=x[j];
end;
end;
x[i]:=max+1;
if x[i]>ans then ans:=x[i];
end;
end;
procedure answer;
begin
assign(output,'nest.out');
rewrite(output);
writeln(ans);
close(output);
end;
begin
init;
solve;
answer;
end.