传送门:POJ 2342
题目大意:
有若干人参加一个聚会,如果两个人之间有直接的上下属关系,则只能去一个。每个人都有个高兴值,问高兴值之和最大是多少?
思路:
之前一直觉得树形DP比较难,现在发现树本身就是递归定义的,并且有子结构,所以在树上使用动态规划是很好的。
这道题可以转化为“父节点和子节点不能同时选择,求节点和的最大值”的问题,我们设: dp[i][0] 表示不选择第 i 个节点时的最大值,dp[i][1] 表示选择第 i 个节点时的最大值。
则可以得到以下式子:
dp[cur][0]+=max(dp[son][1],dp[son][0]); //父节点不选,子节点选或不选
dp[cur][1]+=dp[son][0] //父节点选,子节点不选
代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<vector>
using namespace std;
int a[6010],f[6010],dp[6010][2];
vector<int> edge[6010];
void dfs(int u)
{
int i,v;
for(i=0;i<edge[u].size();i++)
{ //遍历每一个子节点
v=edge[u][i];
dfs(v);
dp[u][0]+=max(dp[v][0],dp[v][1]); //父不去,子去或不去
dp[u][1]+=dp[v][0]; //父去,子不去
}
}
int main()
{
int i,j,n,u,v;
while(~scanf("%d",&n))
{
scanf("%d",&a[1]);
if(n==0&&a[1]==0) break;
for(i=2;i<=n;i++) scanf("%d",&a[i]);
for(i=1;i<=n;i++) edge[i].clear(); //清空
for(i=1;i<=n;i++)
{ //初始化
f[i]=-1; //f[i]表示 i 节点的父节点
dp[i][0]=0;
dp[i][1]=a[i];
}
for(i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
edge[v].push_back(u);
f[u]=v; //记录父节点
}
int root=1;
while(f[root]!=-1) root=f[root]; //寻找根节点
dfs(root);
printf("%d\n",max(dp[root][0],dp[root][1]));
}
return 0;
}