源哥每日一题第十弹 hdu 1520 树形dp

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1520

题意:给你一颗树,每个节点都有一个欢乐值,让你在树上选一些节点,要求节点不能有直接的父子关系,问你在这样的关系下,计算出最大的欢乐值的和。

思路:首先可以确定,每一个点都有两种情况,选或者不选。可以用dp[i][1]表示选这个点之后,在这棵子树中能获得的最大欢乐值,dp[i][0]表示不选这个点,在这棵子树中能获得的最大欢乐值。

很容易想到两个递推式:dp[i][0]+=max(dp[son][1],dp[son][0])爹不选,儿子可选可不选

dp[i][1] += dp[son][0]选了爹就只能不选儿子了。

#include 
#define clr(a,n) memset(a,n,sizeof(a))
#define maxn 6005
using namespace std;
vector E[maxn];
int dp[maxn][2],w[maxn],rt[maxn];

void dfs(int i) {
	dp[i][1] = w[i];
	for (int j = 0; j < E[i].size(); j++) {
		int son = E[i][j];
		dfs(son);
		dp[i][0]+=max(dp[son][1],dp[son][0]);
		dp[i][1]+=dp[son][0];
	} 
}
int main() {
	#ifndef ONLINE_JUDGE
    	freopen("D:\\fengyu\\Jiang_C\\.vscode\\in.txt","r",stdin);
		freopen("D:\\fengyu\\Jiang_C\\.vscode\\out.txt","w",stdout);
	#endif
	int n;
	while(scanf("%d",&n)!=EOF) {
		clr(dp,0);
		clr(rt,-1);
		for (int i = 1; i <= n; i++) {
			scanf("%d",&w[i]);
			E[i].clear();
		}
		int u,v; 
		while(scanf("%d%d",&u,&v),u+v) {
			E[v].push_back(u);
			rt[u] = v;
		}
		int r = 1;
		while(rt[r]!=-1) r = rt[r];
		dfs(r);
		printf("%d\n",max(dp[r][0],dp[r][1]));
	}
	return 0;
}

你可能感兴趣的:(源哥每日一题第十弹 hdu 1520 树形dp)