没有上司的舞会

题目传送门

【思路分析】:

首先我们可以由题意可知,这是一棵树,同时如果作为儿子节点去了舞会,那就说明父亲节点是不会去的;

【状态设计】:

作为一名职员是否愿意参加只跟他的直接上司有关系,所以我们在设状态的时候就可以多开一维来表示是否参加,也就是说树上的节点存储两个信息,第一个就是该职员去参加舞会的最大值,第二个就是该职员不去参加舞会的最大值。所以状态也就是 (f_{i,1})表示该节点的员工去参加的最大值,(f_{i,0})表示不去;

状态转移:

 (f_{i,0}=)(sumlimits_{to in son(i)} max(f_{to,0},f_{to,1}))意为这个不去的时候,他的儿子有两个选择,分别对应着去或者不去,取(Max)
(f_{i,1}=r_i+sumlimits_{to in son(i)} f_{to,0})意为这个节点去的话,那么他的所有儿子都不能去,他的快乐值和他儿子不能去的最大值的 (sum)即为(ans)

【细节问题】:

首先我们肯定是建树,在建树的时候我们要记录一下每一个节点是否有父亲,然后求出根节点,因为题目没保证说是节点(1)就是校长,同时建树的时候建的树为一个反向有向图,题目给定(A o B)表示 (A)(B)的上司,我们在更新的时候,更新的顺序是从儿子更新父亲,所以应该要反过来更新,如果是双向边,先保证不能走回去,不让将会一直更新;

给出代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=1e6;
struct Edge
{
	int nxt,to;
}edge[maxn<<1];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int number_edge,head[maxn<<1];
void add_edge(int from,int to)
{
	number_edge++;
	edge[number_edge].nxt=head[from];
	edge[number_edge].to=to;
	head[from]=number_edge;
}
int n;
int f[maxn][2];
int vis[maxn]; 
void dp(int x)
{
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int to=edge[i].to;
		dp(to);
		f[x][0]+=max(f[to][0],f[to][1]);
		f[x][1]+=f[to][0];
	}
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		f[i][1]=read();
	}
	for(int i=1;i<=n-1;i++)
	{
		int x=read(),y=read();
		vis[x]=1;
		add_edge(y,x);
	}
	int root;
	for(int i=1;i<=n;i++)
	{
		if(!vis[i])
		{
			root=i;
			break;
		}
	}
	dp(root);
	printf("%d",max(f[root][0],f[root][1]));
	return 0;
} 
原文地址:https://www.cnblogs.com/Zmonarch/p/14002602.html