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]);

时间: 2024-08-06 11:58:45

codvs 1163 访问艺术馆 树状动规的相关文章

XJOI1657&amp;Codevs1255搭积木【树状动规】

搭积木 一种积木搭建方式,高为H的积木,最底层有M个积木,每一层的积木数是他的低一层的积木数+1或-1.总共有N个积木.(且每行积木数不超过10)比如上图N=13 H=6 M=2. 输入格式: 第一行为三个整数.N.H.M.第二行以后每行一个整数K,-1为结束符. 输出格式: 第一行为满足N.H.M的积木搭建方案总数(1<=N<=540 H<=60 M<=10)以后每一行对于对应的K,给出顺序排列的第K种方案(最小的排列为第一种). 样例输入: 13 6 2 1 3 -1 样例输出

wikioi 1163 访问艺术馆 树形dp

递归建树,由题知该树是一棵二叉树,且除根节点外其他点的度为0或2. dp[i][j]表示来到第i个走廊(还未走过这条走廊)还剩下j时间,能拿到最大的画的数量. dp[i][j]=max(dp[i][j],dp[lson[i]][k]+dp[rson][last_time-k]) #include<cstdio> #include<algorithm> using namespace std; int dp[200][700]; int id=0,x,y,s; struct node

1163 访问艺术馆

时间限制: 1 s 空间限制: 128000 KB 题目等级 : 大师 Master 题解 查看运行结果 题目描述 Description 皮尔是一个出了名的盗画者,他经过数月的精心准备,打算到艺术馆盗画.艺术馆的结构,每条走廊要么分叉为二条走廊,要么通向一个展览室.皮尔知道每个展室里藏画的数量,并且他精确地测量了通过每条走廊的时间,由于经验老道,他拿下一副画需要5秒的时间.你的任务是设计一个程序,计算在警察赶来之前(警察到达时皮尔回到了入口也算),他最多能偷到多少幅画. 输入描述 Input

codevs1163访问艺术馆(树形dp)

1163 访问艺术馆 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 皮尔是一个出了名的盗画者,他经过数月的精心准备,打算到艺术馆盗画.艺术馆的结构,每条走廊要么分叉为二条走廊,要么通向一个展览室.皮尔知道每个展室里藏画的数量,并且他精确地测量了通过每条走廊的时间,由于经验老道,他拿下一副画需要5秒的时间.你的任务是设计一个程序,计算在警察赶来之前(警察到达时皮尔回到了入口也算),他最多能偷到多少幅画. 输入描述 Input

codevs 访问艺术馆

/* codevs 1163 访问艺术馆 红果果的树形dp*/ #include<iostream> #include<cstdio> #include<cstring> #define maxn 210 using namespace std; int n,m,lc[maxn],rc[maxn],g[maxn][2],T,v[maxn],f[maxn][maxn*6],x,y; struct node{ int v,t,pre; }e[maxn*2]; void df

树状数组维护前缀和

树状数组是用来维护序列前缀和的数据结构.它的修改与求和都是O(logn)的,效率非常高. 我们设序列为A,则树状数组c中,c[i]记录序列A的区间[ i-lowbit(i)+1 , i ]中所有数的和. (树状数组是个好东西ovo)  树状数组在进行区间操作时,要从上到下访问,进行单点操作时,要从下到上访问.  树状数组维护序列前缀和的模版如下: #include <iostream> #include <cstdio> #define maxn 500005 using name

访问艺术馆 (Codevs No.1163)

2016-05-31 20:48:47 题目链接: 访问艺术馆 (Codevs No.1163) 题目大意: 一个贼要在一个二叉树结构的艺术馆中偷画,画都处于叶子节点处,偷画和经过走廊都需要时间,求在限定时间内可以偷到最大数量 解法: 树状DP (记忆化搜索实现) DP[i][j]表示到达i节点时还有j的时间来移动可以偷到的最大数量 状态转移: 对于叶子节点,直接按时间剩余返回最大偷画数量 对于非叶子节点,最大值可能来自Lson一边,也可能只来自Rson一边,还有可能是Lson,Rson按某种方

代码与算法集锦-归并排序+树状数组+快排+深度优先搜索+01背包(动态规划)

归并排序 求逆序数 归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 首先考虑下如何将将二个有序数列合并.这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数.然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可. //将有序数组a[]和b[]合并到c[]中 void MemeryArray(int a[], int n, int b[], int m, int c

树状DP入门

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1520 题目大意:给定一棵关系树,每个节点有个权值,子节点和父节点不能同时选,问最后能选的最大价值是多少? 解题思路:树形DP入门题.由于子节点与父节点不能同时选,有人可能会用贪心思想,二者选其一肯定最优.其实不然,有可能父节点和子节点都不选,而要选子孙节点.不过只要再往深点想下,就可以得出动态规划的解法.每个节点要么选要么不选,和大多数选不选动归一样,来个dp[i][2],0表示不选,1表示不选,那