最大子树和
题目描述
小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花卉的问题。于是当日课后,小明就向老师提出了这个问题:
一株奇怪的花卉,上面共连有N 朵花,共有N−1条枝干将花儿连在一起,并且未修剪时每朵花都不是孤立的。每朵花都有一个“美丽指数”,该数越大说明这朵花越漂亮,也有“美丽指数”为负数的,说明这朵花看着都让人恶心。所谓“修剪”,意为:去掉其中的一条枝条,这样一株花就成了两株,扔掉其中一株。经过一系列“修剪“之后,还剩下最后一株花(也可能是一朵)。老师的任务就是:通过一系列“修剪”(也可以什么“修剪”都不进行),使剩下的那株(那朵)花卉上所有花朵的“美丽指数”之和最大。
老师想了一会儿,给出了正解。小明见问题被轻易攻破,相当不爽,于是又拿来问你。
输入格式
第一行一个整数N(1≤N≤16000)。表示原始的那株花卉上共N朵花。
第二行有N个整数,第I个整数表示第I朵花的美丽指数。
接下来N−1行每行两个整数a,b,表示存在一条连接第a 朵花和第b朵花的枝条。
输出格式
一个数,表示一系列“修剪”之后所能得到的“美丽指数”之和的最大值。保证绝对值不超过2147483647
输入案例
7
-1 -1 -1 1 1 1 0
1 4
2 5
3 6
4 7
5 7
6 7
输出案例
3
题目大意
有很多花在不同的枝干上,每个花都有权值,可以剪去丑花或者整个丑枝干减去,使整体的花枝美丽指数最大.
算法分析
树形dp,每次遍历判断当前f[i]是否为正,如果正就加上,得到最大值;
代码实现
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<map>
#include<set>
#include<string>
#include<stack>
#include<queue>
#include<vector>
#include<math.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long int ll;
const int maxn=1e5+5;
struct edge
{
int pre,to;
}e[maxn];
//e[i].pre表示与第i条边同起点的上一条边;
//e[i].to表示第i条边的终点
int head[maxn],sum=0;//head数组记录该i点的最后一条关联边
int f[maxn],a[maxn];//f数组为当前到i的美丽指数的和,a数组为单个权值
int cnt=0;
void add(int x,int y)//链式前向星存图
{
e[++cnt].pre=head[x];
e[cnt].to=y;
head[x]=cnt;
}
void dfs(int sx,int fx)//dfs遍历图
{
f[sx]=a[sx];//每个枝节都带本身的结点权值
for(int i=head[sx];i;i=e[i].pre)
{
int s=e[i].to;
if(s!=fx)//没有到达终点的话继续递归遍历
{
dfs(s,sx);
f[sx]+=max(0,f[s]);//除去负数(不好看的丑花)
}
}
sum=max(sum,f[sx]);//更新最大值
}
int main()
{
int N,q;
cin>>N;
for(int i=1;i<=N;i++)//存权值
{
cin>>q;
a[i]=q;
}
int a,b;
for(int i=1;i<=N-1;i++)//n个点n-1条边
{
cin>>a>>b;
add(a,b);//无向边,两个点都存一下
add(b,a);
}
dfs(1,0);//fx取0是为了遍历所有的边;
cout<<sum<<endl;
return 0;
}