二、线性动态规划
上次只能算一个热身,这次是真正的动态规划了。还是洛谷(http://www.luogu.org/problem/show?pid=1048):
题目描述 Description
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入输出格式 Input/output
输入格式:
输入文件medic.in的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出格式:
输出文件medic.out包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
按原来思路,枚举每一种情况,因为每一株草药都可以取或是不取,所以共有2^M种情况,通过calc不难算出2^100=1267650600228229401496703205376,按一秒运行10^8次、一年365天计算,401969368413315年就能计算完了!而再加上各种优化,就算舍掉零头,还有400万亿年,而宇宙估计还能存在240亿年,所以这个程序在极端条件下会运行到宇宙毁灭,而即使你把这个程序放到天河二号上,一直都是最快运算,每秒5.49亿亿次,也要732185年(人类出现到现在也只有5000000年左右)。
话题扯远了~~
那如何节省时间到1s以内呢?
像上次一样,我们应该记录下原来的搜索中有用的部分。
显然,花一定的时间所能得到的最大价值是一定的(正如在一个点能滑的最远距离是一定的)
所以f[x]为用x时间得到的最大价值,
则f[j]=max{f[j],f[j-w[i]]+v[i]}
i枚举每一件物品,v[i]为他的价值,w[i]为他的时间,j枚举0~T的所有时间
完了?
没玩!
如果j从0开始枚举,程序会在f[0]处加入v[i];f[w[i]]处再加入……
所以一棵草药会被采很多次(画外音:呜呜呜呜呜呜)
So,从T倒回到0.
简洁精炼的程序:
var
t,m,i,j,x,u:longint;
a:array[0..1000] of longint;
begin
readln(t,m);
for i:=1 to m do
begin
readln(x,u);
for j:=t downto x do
if a[j]<a[j-x]+u then
a[j]:=a[j-x]+u;
end;
writeln(a[t]);
end.