题目链接: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 650 int 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; }