Wiki OI 1163 访问艺术馆

7 篇文章 0 订阅
6 篇文章 0 订阅

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值