【ybtoj高效进阶5-4-1】 树上求和

这篇博客介绍了一种使用树形动态规划(树形DP)解决树上节点求和最大化的算法。给定一棵树,目标是选择一些节点,使得它们的和最大,但条件是选择了父节点就不能选择其子节点。通过定义两个状态表示是否选择当前节点,采用逆推方式从叶子节点处理到根节点,最后输出根节点的最大和作为答案。
摘要由CSDN通过智能技术生成

【ybtoj高效进阶5-4-1】 树上求和

题目大意:

给你一棵树,给出节点的父子关系 ,让你从中选出一些节点,让他们的和最大,然后如果选择了父节点就不能选择他们的子节点,问这个最大的和是多少?

思路:

树形DP,我们要用逆推,,从叶子节点一直处理到根节点。
我们设 f [ i ] [ 0 ] f[i][0] f[i][0]表示处理到第 i 个点时不选这个点的最大值, f [ i ] [ 1 ] f[i][1] f[i][1]表示选了这个点的最大值,son(i)是i的子节点集合。
那么取了该节点我们就不能取它的子节点,如果没取那么就可取可不取,
所以状态转移方程是: f [ i ] [ 0 ] + = m a x ( f [ j ] [ 0 ] , f [ j ] [ 1 ] ) f[i][0]+=max(f[j][0],f[j][1]) f[i][0]+=max(f[j][0],f[j][1]) f [ i ] [ 1 ] + = f [ j ] [ 0 ] f[i][1]+=f[j][0] f[i][1]+=f[j][0] 其 中 j ∈ s o n ( i ) 其中j\in son(i) json(i)
那么最后的的答案就是 m a x ( f [ r o o t ] [ 0 ] , f [ r o o t ] [ 1 ] ) max(f[root][0],f[root][1]) max(f[root][0]f[root][1]),其中   r o o t   ~root~  root 是这棵树的根节点

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#define r register
#define rep(i,x,y) for(r ll i=x;i<=y;++i)
#define per(i,x,y) for(r ll i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const ll V=6*1e3+10;
struct node
{
	ll val,son[V],tot;
}tree[V];
ll n,x,y,f[V][2],fa[V];
void dp(ll now)
{
	f[now][1]=tree[now].val;
	f[now][0]=0;
	if(tree[now].tot==0) return ;
	rep(i,1,tree[now].tot) //tot[i]:i节点子节点的个数
	{
		ll y=tree[now].son[i];
		dp(y); //逆推从后往前,从叶子节点向根节点
		f[now][0]+=max(f[y][0],f[y][1]);
		f[now][1]+=f[y][0];
	}
}
int main()	
{
	scanf("%lld",&n);
	rep(i,1,n)
	 scanf("%lld",&tree[i].val);
	rep(i,1,n-1)
	{
		scanf("%lld%lld",&x,&y);
		tree[y].son[++tree[y].tot]=x; //记录子节点
		++fa[x];
	}
	rep(i,1,n)
	 if(fa[i]==0) //根节点
	 {
	 	dp(i);
	 	printf("%lld",max(f[i][0],f[i][1]));
	 	break ;
	 } 
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值