Wiki OI 1163 访问艺术馆

来源:互联网 发布:mac智能文件夹怎么用 编辑:程序博客网 时间:2024/04/30 10:15

题目链接:http://wikioi.com/problem/1163/

算法与思路:树形dp,dfs;

将博物馆的结构抽象成一棵二叉树,每条边都有对应的权值(走过这条边花费的时间),

只在叶子节点有藏画,要求你在有限的时间内偷到尽可能多的藏画。

点的信息按照深度优先顺序给出(前序遍历),建立一颗二叉树;

然后从根节点开始深搜,每走过一条走廊到达下一个点,

剩余的时间remain要减去2倍这条走廊的花费,相当于一去一回;

一旦走到叶子节点,尽可能多拿藏画;

状态转移方程dp[root][time] = max{dp[lson][i] + dp[rson][remain - i]}    (0 <= i <= remain <= time)

表示在以root为根节点的树中,在时间time内偷到的最多的藏画数;

在当前剩余时间下,枚举在左孩子花费的时间i,剩余的时间分配给右孩子;

并求出所有分配方案下偷到的藏画的最大值。

代码实现:

#include<stdio.h>#include<string.h>using namespace std ;#define N 6500#define T 650int n, m;struct node {int lson, rson, val, cost; }tree[N];struct data{int t, v;}buf[N];int dp[N][T];int max(int x, int y){return x > y ? x : y; }void build(int &x)     //按照深度优先顺序(前序遍历)建树 {int root = x;tree[root].cost = 2 * buf[root].t ; //减去经过走廊的来回时间花费tree[root].val = buf[root].v;if(buf[x].v)              //判断是否是叶子节点 {tree[root].lson = tree[root].rson = -1;    //没有左右孩子 return;}tree[root].lson = x + 1;         //左孩子是下一个节点 build(++x);             //以左孩子为根节点继续建树 tree[root].rson = x + 1;         //右孩子是下一个节点build(++x);               //以右孩子为根节点继续建树}int dfs(int root, int time)      // dp[root][time]表示在root这个节点(岔口),剩余time时能偷到的最大副画 {if(dp[root][time] != -1) return dp[root][time];    //如果有答案,就结束返回 if(time == 0)    return dp[root][time] = 0;      //时间还剩0,则一副也偷不到 if(tree[root].lson == -1)          //是叶子 {int cnt;if(tree[root].val * 5 <= time - tree[root].cost)   cnt = tree[root].val ;      //如果经过走廊后的剩余时间可以全部偷完画,直接赋值 else  cnt = (time - tree[root].cost) / 5 ;          //偷不完就尽可能多拿 return dp[root][time] = cnt;      //返回结果 }  dp[root][time] = 0;      //初始化 int remain = time - tree[root].cost ;    //经过走廊后的剩余时间, for(int i = 0; i <= remain; i++)      //枚举分配一部分时间给左孩子,剩余时间往右孩子那边去偷 { int s1 = dfs(tree[root].lson, i);          //往左孩子方向偷的最多的画  int s2 = dfs(tree[root].rson, remain - i);     //往右孩子那边偷的最多的画 dp[root][time] = max(dp[root][time], s1 + s2);   //遍历寻求最大值 }         return dp[root][time];          }int main(){    int time; n = m = 0;   scanf("%d", &time);   while(scanf("%d %d", &buf[n].t, &buf[n].v)!=EOF)   n++;    build(m);     //以0为根节点建树   memset(dp, -1, sizeof(dp));  dfs(0, time);  printf("%d", dp[0][time]);  //0节点,剩余time时间能偷到的最大画就是结果   return 0;}