〖题目〗
“访问”艺术馆
【问题描述】
经过数月的精心准备,Peer Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。Peer知道每个展室里藏画的数量,并且他精确测量了通过每条走廊的时间。由于经验老到,他拿下一幅画需要5秒的时间。你的任务是编一个程序,计算在警察赶来前,他最多能偷到多少幅画。
【输入格式】gallery.in
第1行是警察赶到的时间,以s为单位。第2行描述了艺术馆的结构,是一串非负整数,成对地出现:每一对的第一个数是走过一条走廊的时间,第2个数是它末端的藏画数量:如果第2个数是0,那么说明这条走廊分叉为两条另外的走廊。数据按照深度优先的次序给出,请看样例。
一个展室最多有20幅画。通过每个走廊的时间不超过20s。艺术馆最多有100个展室。警察赶到的时间在10min以内。
【输出格式】gallery.out
输出偷到的画的数量。
【输入样例】
60
7 0 8 0 3 1 14 2 10 0 12 4 6 2
【输出样例】
2
【限制】 内存:50 M 时间:1 S
【评测方式】
【数据规模】
〖分析〗
仔细思索下题目,其实不难发现,这道题目的主要问题在2个方面:“建树”和“计算”。“建树”相对来说比较容易,只要利用递归就好了。当然我是使用指针的,毕竟感觉上这样更省空间,而且它也没说有多少条走廊之类的。(在NOI或NOIP或别的上,能不用指针就不用指针。虽然指针功能强大,但是“失之毫厘,谬之千里”,走错一步就满盘皆输)所以,只需要读入两个数T(走过一条走廊的时间),S(它末端的藏画数量)。若S=0 则继续递归,反之,回溯。
简简单单,树建好了。然后就是计算了。计算,刚开始认为是贪心,后来觉得贪心不对,就想搜索。搜索编着编着忽然灵光一现:DP。确实,我是用DP过的——树状DP。公式很简单:f(root,tt)=max(f(left,k)+f(right,tt-t*2-k)),其中f(root:tree;tt:longint)表示Peer Brelstet在tt秒内在二叉数root内所能得到的最多的画的数目。当然,边界条件就是当root下没有子树的时候,就去拿画:f(root,tt)=min((tt-2*t)div 5 , s)。也就是说,在tt秒内尽可能去拿画,拿完了为止。
因此,最后的答案不言而喻。不过这样,只能拿75分(3/4)。经过反复检查、验证。原因就是:“在警察赶来前”,也就是说不能在警察刚好赶到的时候离开。解决方法:在输入时将“警察赶到的时间”减1。问题就解决了!
至此,《“访问”艺术馆》正式解决。
〖程序〗
// TASK: gallery
type
tree=^point;
point=record
s,t:longint;
left,right:tree;
f,p:array [0..600] of longint;
end;
var
f:text;
num:longint;
r:tree;
procedure init;
var
i,j,k:longint;
procedure maketree(var root:tree);
begin
read(f,root^.t,root^.s);
with root^ do
begin
inc(t,t);
if s>0 then
begin
left:=nil;right:=nil;
exit;
end else begin
new(left);new(right);
maketree(left);
maketree(right);
end;
end;
end;
begin
assign(f,'gallery.in');reset(f);
readln(f,num);
dec(num);
new(r);
maketree(r);
close(f);
end;
procedure ouot;
begin
assign(f,'gallery.out');rewrite(f);
writeln(f,r^.f[num]);
close(f);
end;
procedure calc(root:tree;sum:longint);
var
i,j,k:longint;
begin
with root^ do
begin
fillchar(f,sizeof(f),0);
fillchar(p,sizeof(p),0);
if sum<=t then exit;
if s>0 then
begin
for i:=t to sum do
begin
k:=(i-t) div 5;
if k<s then f[i]:=k
else f[i]:=s;
end;
end else begin
calc(left,sum-t);calc(right,sum-t);
for i:=t to sum do
for k:=0 to i-t do
begin
j:=left^.f[k]+right^.f[i-t-k];
if f[i]<j then
begin
p[i]:=k;
f[i]:=j;
end;
end;
end;
end;
end;
begin
init;
calc(r,num);
ouot;
end.