codvs 1163 访问艺术馆 树状动规

【问题描述】 
经过数月的精心准备,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 
【输出样例】 

【时间限制】 
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]);

posted on 2015-11-26 21:40  83131  阅读(336)  评论(0编辑  收藏  举报

导航