2015蓝桥杯B组:生命之树(树形DP)

在 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


赛场上没写对,感觉好失望啊。

思路:树上的动态规划问题,考虑结点i,dp[i] :以i为父结点,与子结点组成的集合中整数和的最大值;

得到状态转移方程dp[u] += dp[v] {v | v是u的子结点 && dp[v] > 0},dp[i]的初始值就是刚开始结点i的数值;

结果需要求最大和,所以求出dp[i] (i 属于 n) 的最大值即可

/***********************************************
 * Author: fisty
 * Created Time: 2015/4/12 16:09:37
 * File Name   : aa.cpp
 *********************************************** */
#include <iostream>
#include <cstring>
#include <deque>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <algorithm>
using namespace std;
#define Debug(x) cout << #x << " " << x <<endl
#define Memset(x, a) memset(x, a, sizeof(x))
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef pair<int, int> P;
#define FOR(i, a, b) for(int i = a;i < b; i++)
#define MAX_N 100100
int a[MAX_N];
vector<int> G[MAX_N];
int vis[MAX_N];
LL dp[MAX_N],ans = 0;

void dfs(int u){
    vis[u] = 1;
    for(int i = 0;i < G[u].size(); i++){
        int v = G[u][i];
        if(!vis[v]){
            dfs(v);
            if(dp[v] > 0)
                dp[u] += dp[v];
        }
    }
    ans = max(dp[u], ans);
    return ;
}
int main() {
    //freopen("in.cpp", "r", stdin);
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n;
    cin >> n;
    Memset(dp, 0);
    for(int i = 1;i <= n; i++){
        cin >> a[i];
        dp[i] = a[i];
    }
    for(int i = 1;i < n; i++){
        int u, v;
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    Memset(vis, 0);
    dfs(1);
    cout << ans << endl;
    return 0;
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值