[经典问题]“访问”艺术馆

本文解析了一道经典的算法竞赛题目——艺术馆偷画问题。文章详细介绍了如何使用树状结构来模拟艺术馆布局,以及如何运用动态规划算法求解在限定时间内能够偷取的最大画作数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

〖题目〗

“访问”艺术馆

【问题描述】

    经过数月的精心准备,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.

转载于:https://www.cnblogs.com/klarkxy/archive/2009/07/20/10017209.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值