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