AcWing 285 没有上司的舞会(入门树形dp)

**
原题链接

题目描述

**Ural大学有N名职员,编号为1~N。

他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司。

每个职员有一个快乐指数,用整数 Hi 给出,其中 1≤i≤N。

现在要召开一场周年庆宴会,不过,没有职员愿意和直接上司一起参会。

在满足这个条件的前提下,主办方希望邀请一部分职员参会,使得所有参会职员的快乐指数总和最大,求这个最大值。

输入格式
第一行一个整数N。

接下来N行,第 i 行表示 i 号职员的快乐指数Hi。

接下来N-1行,每行输入一对整数L, K,表示K是L的直接上司。

输出格式
输出最大的快乐指数。

数据范围
1≤N≤6000,
−128≤Hi≤127

输入样例:
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
输出样例
5

题目分析

对于这个题目我们首先要做的就是建图(建树),每个点都有一个权值,但是选择的时候,我们子节点和父节点不能一起选,问选择的可以最大权值是多少?
拿样例来说,我们可以建出这样一颗树

红色区域为最佳选择部分。
我们可以设f(i)为根节点为i的时候最佳选择,这时发现无法进行状态转移,因为每个父亲节点有两种状态,选或者不选,对子节点的影响是不同的
1 选父亲节点的时候。子节点不能选,
2不选父亲节点的时候。字节点可以选,可以不选
所以这里我们加一维,f[i][1]和f[i][0] 分别表示 选父亲节点和不选父亲节点的情况

状态转移
在这里插入图片描述

对于一个节点i有着两个子节点s1 ,s2,

1如果父亲节点i选的话,
f[i][1]=f[s1][0]+f[s2][0];
2如果父亲节点i不选的话,
f[i][0]=max(f[s1][0],f[s1][1])+max(f[s2][0],f[s2][1])

我们注意题目中由n个点。n-1一条边,所以一定是一棵树,
我采取了类似链式前向星的方法存图
AcCode

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define  gold_m main
#define re register
#define Accept return 0;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf =0x3f3f3f3f;
const int maxn=1e6+5;
const int mod = 1e9+7;
const int N=666;
const long double PI=3.1415926535897932384626433832795;
const long double e=2.71828182845904523536028747135266;
typedef pair<int,int>PII;
inline ll read() {
    
    
	ll  x=0;
	bool f=0;
	char ch=getchar();
	while (ch<'0'||'9'<ch)	f|=ch=='-', ch=getchar();
	while ('0'<=ch && ch<='9')
		x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
priority_queue<int,vector<int>, greater<int> >heap;
stack<int>st;
int  dp[maxn][2],cnt,root,head[maxn],h[maxn],n,l,k,rt[maxn];
struct wmy {
    
    
	ll u,v,w,next;
} a[maxn];
void add(ll u,ll v) {
    
    
	a[cnt].u=u;
	a[cnt].v=v;
//	a[cnt].w=w;
	a[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(int  u)
{
    
    
	dp[u][1] =h[u];
	
	for(int i=head[u];i!=-1;i=a[i].next)
	{
    
    
		ll v=a[i].v;
		dfs(v);
		dp[u][1]+=dp[v][0];
		dp[u][0]+=max(dp[v][0],dp[v][1]);
	}
	return ;
}
int   gold_m() {
    
    
	memset(head,-1,sizeof(head));
	n=read();
	for(int i=1 ; i<=n ; i++) h[i]=read();
	for(int i=1 ; i<n; i++) {
    
    
		cin>>l>>k; //有一条由k指向l的线段 
		add(k,l);
		rt[l]++;
	}
	for(int i=1 ; i<=n; i++) {
    
    
		if(rt[i]==0) {
    
    
			root=i;// 如果这个点没有父亲节点,那么这个点一定是根节点,校长节点 
			break;
		}
	}
	dfs(root);
	cout<<max(dp[root][0],dp[root][1]);
	Accept;
}
/*

*/

猜你喜欢

转载自blog.csdn.net/wmy0536/article/details/104445461
今日推荐