生命之树

来源:互联网 发布:java jre1.6 32位下载 编辑:程序博客网 时间:2024/05/19 03:20

在 X 森林里,上帝创建了生命之树。

他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,代表这个点的和谐值。
上帝要在这棵树内选出一个非空节点集 S,使得对于 S 中的任意两个点 a,b,都存在一个点列 {a, v1, v2, …, vk, b} 使得这个点列中的每个点都是 S 里面的元素,且序列中相邻两个点间有一条边相连。

在这个前提下,上帝要使得 S 中的点所对应的整数的和尽量大。
这个最大的和就是上帝给生命之树的评分。

经过 atm 的努力,他已经知道了上帝给每棵树上每个节点上的整数。但是由于 atm 不擅长计算,他不知道怎样有效的求评分。他需要你为他写一个程序来计算一棵树的分数。

「输入格式」
第一行一个整数 n 表示这棵树有 n 个节点。
第二行 n 个整数,依次表示每个节点的评分。
接下来 n-1 行,每行 2 个整数 u, v,表示存在一条 u 到 v 的边。由于这是一棵树,所以是不存在环的。

「输出格式」
输出一行一个数,表示上帝给这棵树的分数。

「样例输入」
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5

「样例输出」
8

「数据范围」
对于 30% 的数据,n <= 10
对于 100% 的数据,0 < n <= 10^5, 每个节点的评分的绝对值不超过 10^6


树形DP。

开始想用暴力搜索,结果搜索也不知道怎么处理那些细节。。。

dp[i]表示:以i为根节点的和最大的子树数值之和(因为是子树,所以一定包含i节点)

所以,dp[i] += dp[k],这里的k是指i的孩子节点,且dp[k]一定要是大于0的,如果小于0,会使总和变小,加它还不如不加。

所以就用一个递归,从一个根节点出发,一直到叶子节点,这时候就回溯了,回溯的时候就会把下层节点的dp值带回到上层。每计算出一个i的dp值,就与当前保存的最大值比较。

#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;typedef struct{int y, next;}edge;edge e[1000003];int i, v[1000003], head[1000003], vis[1000003], n;long long dp[1000003], ans;void dfs(int i){vis[i] = 1;int j, y;for(j = head[i] ; j != -1 ; j = e[j].next){y = e[j].y;if(vis[y])    continue;dfs(y);if(dp[y] > 0)//如果小于0,那么加它的话不会使总和增大,所以直接不加了     dp[i] += dp[y];}ans = max(ans,dp[i]);}int main(){int i, x, y;scanf("%d", &n);for(i = 1 ; i <= n; i++){scanf("%d", &v[i]);head[i] = -1;dp[i] = v[i];}for(i = 0 ; i < n-1; i++){scanf("%d %d", &x, &y);e[i].y = y;e[i].next = head[x];head[x] = i;e[i + n - 1].y = x;e[i + n - 1].next = head[y];head[y] = i + n - 1;}dfs(1);printf("%lld\n", ans);return 0;}



0 0
原创粉丝点击