题目链接
https://vjudge.z180.cn/problem/POJ-2342
题目大意:公司安排一场聚会,你可以安排聚会有哪些人参加,公司里有上下级关系,每个员工都有一个权值(可正可负),但是聚会安排有一个硬性要求,就是,一个员工和他的直接上司不能同时参加聚会,你的任务是,使得参加聚会的人的权值之和尽可能的大,输出最大是多少.
输入:第一行一个整数N,代表公司员工人数
接下来N行,每行一个整数wi,表示第i名员工的权值
接下里N-1行,每行两个整数L,K,表示K是L的直接上司
分析:首先根据关系建立一个树形图,设dp数组为dp[u][2],dp[u][0],表示u这个员工不参加聚会,那么此时他的所有直接下属都可以参加聚会或者不参加状态转移方程dp[u][0] = sum(max(dp[v][0],dp[v][1])),v是u的子节点(也就是直接下属),dp[u][1]表示u这个员工参加聚会,此时他的直接下属就不能参加聚会状态转移方程dp[u][1] = sum(dp[v][0])
建图可以采用链式前向星,不懂的可以现学一下,很简单链式前向星详解
AC代码:
#include<iostream>
#include<cstring>
using namespace std;
const int Maxn = 1e4+10;
/*链式前向星*/
struct Edge
{
int v;
int next;
}edge[Maxn];
int head[Maxn];
int f[Maxn][2];//f[i][1],表示选择当前节点时的最大权值,f[i][0]表示不选当前节点时的最大权值
int w[Maxn];
bool vis[Maxn];//记录节点是否有父亲,找根节点
int sz[Maxn];//记录以u为根结点的树有多少个子节点
int cnt = 0;
/*链式前向星建图*/
void build(int u,int v)
{
edge[++cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt;
return ;
}
void dfs(int u)
{
f[u][1] = w[u];
f[u][0] = 0;
sz[u] = 1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
dfs(edge[i].v);
f[u][1] +=f[edge[i].v][0];
f[u][0] +=max(f[edge[i].v][0],f[edge[i].v][1]);
}
return ;
}
int main()
{
memset(head,-1,sizeof(head));
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>w[i];
int u,v;
while(cin>>v>>u)
{
if(!v&&!u) break;
vis[v] = true;
build(u,v);
}
int node;
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
node = i;
break;
}
}
dfs(node);
cout<<max(f[node][1],f[node][0]);
return 0;
}