#4033. 好吃的QQ(eat)

题目描述

某天,好吃的QQ被火星人莫名其妙的传送到了树形国的首都root,正如其名,树形国就是一棵树,一共n个城市,有n-1条路连接,城市与城市之间都连通

令人吃惊的是,QQ不知道用什么方法,得知了树形国各个城市的小吃种类(可以认为任意两个小吃都是不同种类的),于是乎他想一边游玩树形国,一边品尝各种的小吃,而QQ从一个城市s,走到相邻的一个城市t,当且仅当城市t存在一种他没有吃过的小吃,而他到城市t之后就会立即品尝那一种小吃,却不会品尝该城市其他的小吃

那么,请你为好吃的QQ设计一个路线,使得他能品尝到尽量多种类的小吃,并且最后会回到首都root,以便火星人送他回家

输入格式

第一行一个数n,表示树形国的城市数

第二行n个数表示每个城市的小吃种类

以下n-1行,每行2个数a,b,表示有一条路连接城市a,b

接下来一行一个数root,表示树形国的首都

输出格式

一个数表示QQ最多能吃到多少种小吃

样例
样例输入

5
2 1 1 1 1
1 2
2 3
1 4
4 5
1

样例输出

4

数据范围与提示

【友情提示】
一开始被传送到首都时,QQ并不会品尝首都的小吃,请注意最后要回到首都root
样例路线start>1>2(+)>1(+)>4(+)>1(+)>end

【数据约定】
设t为max{每个城市的小吃种类}
30%数据满足n≤5,t≤5
60%数据满足n≤30,t≤30
100%数据满足n≤100000,0≤t≤2^31

来源

长郡Noip2014模拟1
题解:
树形DP

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100005
using namespace std;
long long f[N],res[N],up[N];
int n,root,head[N],ver[N<<1],nex[N<<1],tot,fa[N],cnt[N];
struct node
{
	int ff;
}temp[N];
bool cmp(node a,node b){
	return a.ff>b.ff;
}
inline void add(int x,int y){
	nex[++tot]=head[x];
	head[x]=tot;
	ver[tot]=y;
}
void tree_dfs(int x){
	up[x]-=1;
	if(x!=root&&up[x]>=0)f[x]+=1;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(fa[x]==y)continue;
		fa[y]=x;
		tree_dfs(y);
	}long long now=0;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(fa[x]==y)continue;
		temp[++cnt[x]].ff=f[y];
		now=now+res[y];
	}
	sort(temp+1,temp+1+cnt[x],cmp);
	for(int i=1;i<=cnt[x];++i){
		if(up[x]<=0)break;
		if(temp[i].ff<=0)break;
		f[x]=f[x]+temp[i].ff+1;up[x]-=1;
	}
	f[x]+=2*max((long long)0,min(now,up[x]));
	res[x]=max((long long)0,up[x]-now);
	//cout<<x<<" "<<f[x]<<" "<<res[x]<<endl;
}
int main()
{
	//freopen("eat.in","r",stdin);
	//freopen("eat.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%lld",&up[i]);
	for(int i=1;i<n;++i){
		int x,y;scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	cin>>root;
	fa[root]=-1;up[root]+=1;
	tree_dfs(root);
	//fa[4]=1;
	//tree_dfs(4);
//	for(int i=1;i<=n;i++)cout<<up[i]<<endl;
	cout<<f[root]<<endl;
	return 0;
}
/*
4
0 3 2 0 
2 3
1 2
4 1
2
*/
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值