P1270 “访问”美术馆

题目描述

经过数月的精心准备,Peer Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。Peer知道每个展室里藏画的数量,并且他精确测量了通过每条走廊的时间。由于经验老到,他拿下一幅画需要5秒的时间。你的任务是编一个程序,计算在警察赶来之前,他最多能偷到多少幅画。

输入输出格式

输入格式:

 

第1行是警察赶到的时间,以s为单位。第2行描述了艺术馆的结构,是一串非负整数,成对地出现:每一对的第一个数是走过一条走廊的时间,第2个数是它末端的藏画数量;如果第2个数是0,那么说明这条走廊分叉为两条另外的走廊。数据按照深度优先的次序给出,请看样例。

一个展室最多有20幅画。通过每个走廊的时间不超过20s。艺术馆最多有100个展室。警察赶到的时间在10min以内。

 

输出格式:

 

输出偷到的画的数量

 

输入输出样例

输入样例#1: 
60
7 0 8 0 3 1 14 2 10 0 12 4 6 2
输出样例#1: 
2

Solution:

  本题是很有技巧性的树形$dp$。。。

  首先我们一定要读题仔细(我开始就没细看搞了半天~),注意几个关键信息:

    1、给定的一定是一棵完满二叉树(所有非叶子结点的度都是2)。  

    2、时间不会超过$600s$ 。

    3、警察来之前取完,意味着求的是$s-1$的时间范围内取的最大价值。  

    4、不要忘了取一件耗费$5s$时间,且一段路要走两次,时间翻倍。

  然后由上面第一条可以得出一个很简单的读入方法:每次读入时,若价值为$0$,则一定有两个子节点,于是递归读入(类似线段树建树)。

  那么我们定义状态$f[i][j]$表示在$i$节点耗费$j$时间能取得的最多件数,不难得到状态转移方程:$f[i][j]=max(f[i][j],f[i<<1][k]+f[i<<1|1][j-k-t[v]])$(在左右子树中取),注意一下搜索时的边界问题就好了。

代码:

 1 #include<bits/stdc++.h>
 2 #define il inline
 3 #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
 4 #define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
 5 #define Max(a,b) ((a)>(b)?(a):(b))
 6 #define Min(a,b) ((a)>(b)?(b):(a))
 7 using namespace std;
 8 const int N=205;
 9 int n,s,w[N],v[N],cnt,f[N][N<<2];
10 
11 il void init(int u){
12     cin>>v[u]>>w[u];
13     v[u]<<=1;
14     if(!w[u]) init(u<<1),init(u<<1|1);
15 }
16 
17 il int dfs(int u,int t){
18     if(f[u][t]||!t) return f[u][t];
19     if(w[u])return f[u][t]=Min(w[u],(t-v[u])/5);
20     For(k,0,t-v[u]) f[u][t]=Max(f[u][t],dfs(u<<1,k)+dfs(u<<1|1,t-v[u]-k));
21     return f[u][t];
22 }
23 
24 int main(){
25     ios::sync_with_stdio(0);
26     cin>>s;
27     init(1);
28     cout<<dfs(1,s-1);
29     return 0;
30 }

猜你喜欢

转载自www.cnblogs.com/five20/p/9166773.html
今日推荐