**
原题链接
题目描述
**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;
}
/*
*/