【问题描述】
经过数月的精心准备,Peer Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,
每条走廊要么分叉为两条走廊,要么通向一个展览室。Peer知道每个展室里藏画的数量,并且他精确测量了通
过每条走廊的时间。由于经验老到,他拿下一幅画需要5秒的时间。你的任务是编一个程序,计算在警察赶来前
,他最多能偷到多少幅画。
【输入格式】
第1行是警察赶到的时间,以秒为单位。第2行描述了艺术馆的结构,是一串非负整数,成对地出现:每一
对的第一个数是走过一条走廊的时间,第2个数是它末端的藏画数量:如果第2个数是0那么说明这条走廊分叉为
两条另外的走廊。数据按照深度优先的次序给出,请看样例。
一个展室最多有20幅画。通过每个走廊的时间不超过20秒。艺术馆最多有100个展室。警察赶到的时间在
10分钟以内。
【输出格式】
输出画的数量。
【输入样例】(见下图)
60
7 0 8 0 3 1 14 2 10 0 12 4 6 2
【输出样例】
2
【时间限制】
1s
【空间限制】
64M
一开始看到这道题着实没有思路,但很快也反应过来是一棵二叉树,但又不知道怎么去做,还想到了网络流去,但又觉得不可能。无奈挣扎之下,百度了。在看到了“
分析:f[i,j]表示以i为根的子树偷j幅画所需的最小时间,最后答案即满足f[root,k]<limit的最大的k.转移时枚举左右子树各偷几幅.” 这两句话之后顿时茅塞顿开,突然觉得解题的思路的确很重要。
最后由于细节的处理,这道题也敲了一个多小时。算了,毕竟还太年轻,没有什么经验。加油吧!
#include<cstdio> #include<iostream> #include<cstring> #define maxn 3000 #define ce 2999 #define rep(i,j,k) for(int i = j; i <= k; i++) #define down(i,j,k) for(int i = j; i >= k; i--) using namespace std; int f[maxn][maxn] = {0}, maxl; int read() { int s = 0, t = 1; char c = getchar(); while( !isdigit(c) ){ if( c == ‘-‘ ) t = -1; c = getchar(); } while( isdigit(c) ){ s = s * 10 + c - ‘0‘; c = getchar(); } return s * t; } void solve(int num) { int timx = read(); int huax = read(); if( !huax ) { solve(num*2); rep(i,1,f[num*2][ce] ) { f[num*2][i] += 2 * timx; } } else { rep(i,1,huax) { f[num*2][i] = 2 * timx + i * 5; } f[num*2][ce] = huax; } int timy = read(); int huay = read(); if( !huay ) { solve(num*2+1); rep(i,1,f[num*2+1][ce] ) { f[num*2+1][i] += 2 * timy; } } else { rep(i,1,huay) { f[num*2+1][i] = 2 * timy + i * 5; } f[num*2+1][ce] = huay; } int maxl = f[num*2][ce] + f[num*2+1][ce]; rep(i,1,maxl){ rep(j,max(0,i-f[num*2][ce]),min(i,f[num*2+1][ce])){ f[num][i] = min(f[num][i],f[num*2+1][j]+f[num*2][i-j]); } } f[num][ce] = maxl; } int main() { memset(f,127,sizeof(f)); rep(i,0,ce) f[i][0] = 0; int t = read(); int tim = read(), hua = read(); if( !hua ) { solve(1); rep(i,1,f[1][ce]) f[1][i] += 2 * tim; } else { rep(i,1,hua){ f[1][i] = 2 * tim + 5 * i; } } down(i,f[1][ce],1){ if( f[1][i] <= t ) { cout<<i<<endl; return 0; } } }
不过,后来,又看到另外一种解题思路:
解题思路:
1>把展览馆看成一颗二叉树。每一条走廊看成是一个节点,有分叉则有左右节点。同时每个节点包含四个信息即左右子树根序、通过这个节点(走廊)耗费的时间以及通过该节点可以获取到的藏画数目(最多)。构建这棵树的时候要使用深度优先搜索算法,因为题目输入便是这种算法得到的序列。
2>这道题目最终要在这棵树上进行动态规划。令dp[root][time]表示使用time的时间到以root为根的二叉树上去偷藏画最多可以偷多少。那么便有如下几种可能:
如果time为0,即没有时间,显然dp[root][time]=0,偷不到藏画;
如果root为叶子节点,即已经没有左右子树了,那么通过叶子节点便可以偷了。所以dp[root][time]="剩余时间 最多可以偷的藏画数目"。这里只要有时间就尽可能多的取藏画,因为所有的取藏画时间都是5秒,所以一定不会 傻逼到有藏画不取还非要移动一个窝去取。
如果root为中间走廊,即还有两个子分支。那么dp[root][time]=max(dp[left][i]+dp[right][remain-i]);