小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花卉的问题。于是当日课后,小明就向老师提出了这个问题:
一株奇怪的花卉,上面共连有NNN朵花,共有N−1N-1N−1条枝干将花儿连在一起,并且未修剪时每朵花都不是孤立的。每朵花都有一个“美丽指数”,该数越大说明这朵花越漂亮,也有“美丽指数”为负数的,说明这朵花看着都让人恶心。所谓“修剪”,意为:去掉其中的一条枝条,这样一株花就成了两株,扔掉其中一株。经过一系列“修剪“之后,还剩下最后一株花(也可能是一朵)。老师的任务就是:通过一系列“修剪”(也可以什么“修剪”都不进行),使剩下的那株(那朵)花卉上所有花朵的“美丽指数”之和最大。
老师想了一会儿,给出了正解。小明见问题被轻易攻破,相当不爽,于是又拿来问你。
第一行一个整数N(1≤N≤16000)N(1 ≤ N ≤ 16000)N(1≤N≤16000)。表示原始的那株花卉上共NNN朵花。
第二行有NNN个整数,第III个整数表示第III朵花的美丽指数。
接下来N−1N-1N−1行每行两个整数a,ba,ba,b,表示存在一条连接第aaa 朵花和第bbb朵花的枝条。
一个数,表示一系列“修剪”之后所能得到的“美丽指数”之和的最大值。保证绝对值不超过214748364721474836472147483647。
7
-1 -1 -1 1 1 1 0
1 4
2 5
3 6
4 7
5 7
6 7
3
【数据规模与约定】
对于60%60\%60%的数据,有N≤1000N≤1000N≤1000;
对于100%100\%100%的数据,有N≤16000N≤16000N≤16000。
这道题简单的来说就是给你一个点带权的树,点权可能是负数,我们现在要找到一个最大子树和。
这道题其实很像线性DP中最大区间和。但是这道题难点就在于你选了某个子节点就必须去选该点所对的父节点。
f[u]f[u]f[u]表示以uuu为根的树,在选择了uuu的情况下,组成的最大子树和。
对于以uuu为根节点的子树来说,它的状态是由子节点构成的子树转移过来的,我们当前需要判断的就是这个子树选还是不选,为了保证和最大,如果当前子节点构成的子树的最大子树和是负数,我们肯定就不选,如果是正数我们肯定是选的。依据这个原则我们即可写出转移方程。由于节点uuu是必须选的,所以我们还得加上根节点uuu的权值。
f[u]=nums(u)+∑(max(f[son],0))f[u] = nums(u)+\sum \bigg(max(f[son], 0)\bigg) f[u]=nums(u)+∑(max(f[son],0))
这里要注意的是,题目中并没有说是以祖宗节点为根的最大子树和。所以我们需要去遍历每个节点。
DFS遍历一遍即可。
#include#define endl '\n'#define INF 0x3f3f3f3fusing namespace std;typedef long long ll;typedef pair pii;const int N = 16000 + 10;vectoredge[N];int beau[N], f[N];int root = 0;int n;void dp(int u, int father){f[u] = beau[u];for(int i = 0; i < edge[u].size(); i ++ ){int son = edge[u][i];if(son == father)continue;dp(son, u);f[u] += f[son] > 0 ? f[son] : 0;}}void solve(){cin >> n;for(int i = 1; i <= n; i ++ )cin >> beau[i];for(int i = 0; i < n - 1; i ++ ){int a, b;cin >> a >> b;edge[a].push_back(b);edge[b].push_back(a);}dp(1, 0);int ans = -INF;for(int i = 1; i <= n; i ++ ){ans = max(f[i], ans);//cout << f[i] << endl;}cout << ans << endl;}int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);solve();}