洛谷P1270-“访问”美术馆(树上背包问题)

题目链接:https://www.luogu.com.cn/problem/P1270
CSDN食用链接:https://blog.csdn.net/qq_43906000/article/details/109231357

题目描述

经过数月的精心准备,Peer Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。Peer知道每个展室里藏画的数量,并且他精确测量了通过每条走廊的时间。由于经验老到,他拿下一幅画需要5秒的时间。你的任务是编一个程序,计算在警察赶来之前,他最多能偷到多少幅画。
在这里插入图片描述

输入格式

第1行是警察赶到的时间,以s为单位。第2行描述了艺术馆的结构,是一串非负整数,成对地出现:每一对的第一个数是走过一条走廊的时间,第2个数是它末端的藏画数量;如果第2个数是0,那么说明这条走廊分叉为两条另外的走廊。数据按照深度优先的次序给出,请看样例。

一个展室最多有20幅画。通过每个走廊的时间不超过20s。艺术馆最多有100个展室。警察赶到的时间在10min以内。

输出格式

输出偷到的画的数量

输入输出样例
输入
60
7 0 8 0 3 1 14 2 10 0 12 4 6 2
输出
2

emmm,这题有两个比较恶心的点,一个是输入,一个就是题意。问在警察赶来之前,他最多能偷到多少幅画,那么隐含的一个条件就是他要返回出口的。。。。还有一个就是必须要严格小于,也就是在规定时间之前到达出口。

建图的话我们可以借鉴一下线段树动态开点的思想:

void build(int fa,int u)
{
	if (u==-1) return;
	if (!vis[u]) {u=++tot;vis[u]=1;}
	int s,nb;
	scanf ("%d%d",&s,&nb);
	val[u]=nb;
	add(fa,u,s); add(u,fa,s);
	if (nb) build(1,-1);
	else build(u,0),build(u,0);
}

接下来就是树上操作了,由于他要求要返回,而且拿画也需要花费时间,所以我们不能直接简单粗暴地讲时间除以2作为上限去找答案。我们只需要将路的长度乘以2就可以达到目的了。接下来就是初始化叶子节点了,我们只需要将时间除以5再取总的最小值就可以得到每个叶子在每个时间下能够取得的最多画了。然后接下来就是一个简单的树上01背包问题了,设\(dp[i][j]\)表示第\(i\)个节点在时间为\(j\)的情况下最多能够拿到的画,那么树上的转移方程也就很容易得到:\(dp[u][j]=max(dp[u][j],dp[v][k]+dp[u][j-k-eg[i].w])\)

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

const int mac=1e4+10;

struct node
{
	int to,next,w;
}eg[mac];
int tot=0,num=0,head[mac];
int val[mac],vis[mac];
int dp[mac][605],tims;

void add(int u,int v,int w)
{
	eg[num]=node{v,head[u],w*2};
	head[u]=num++;
}

void build(int fa,int u)
{
	if (u==-1) return;
	if (!vis[u]) {u=++tot;vis[u]=1;}
	int s,nb;
	scanf ("%d%d",&s,&nb);
	val[u]=nb;
	add(fa,u,s); add(u,fa,s);
	if (nb) build(1,-1);
	else build(u,0),build(u,0);
}

void dfs(int fa,int u)
{
	for (int i=head[u]; i!=-1; i=eg[i].next){
		int v=eg[i].to;
		if (v==fa) continue;
		dfs(u,v);
		for (int j=tims; j>=eg[i].w; j--){
			for (int k=0; k<=j-eg[i].w; k++)
				dp[u][j]=max(dp[u][j],dp[v][k]+dp[u][j-k-eg[i].w]);
		}
	}
}

int main(int argc, char const *argv[])
{
	scanf ("%d",&tims);
	tims--;
	tot=1;
	memset(head,-1,sizeof head);
	vis[1]=1;
	build(1,0);
	for (int i=1; i<=tot; i++) 
		if (val[i]){
			for (int j=0; j<=tims; j++)
				dp[i][j]=min(val[i],j/5);
		}
	dfs(-1,1); 
	printf("%d\n",dp[1][tims]);
	return 0;
}
posted @ 2020-10-22 21:52  lonely_wind  阅读(120)  评论(0编辑  收藏  举报