题目链接:Long Live the Queen
题意是给一棵树,每个结点都有一个值,从这棵树中选出一颗子树来,使得该子树的所有结点的值得和最大。
容易想到解决思路:定义dp[i]表示以结点i为根的子树中可以获得的最大值。
设i的孩子保存在vector: G[i]中,那么:
初始化:dp[i] = value[i],因为定义的是以该结点为根,所以该结点必须包含。
转移:dp[i] = dp[i] + sum{dp[G[i][j]] > 0},其中G[i][j]表示i的某个孩子,并且dp[G[i][j]] > 0时才加到dp[i]上。
当然,我们要先对i的孩子递归处理完毕以后再来处理i,因为这样孩子的dp值就可以先被更新,转移也才有意义。
最后,只需要遍历dp数组,找出以某个点为根的子树的最大值即可。
另外注意:
① 由于树的性质,我们可以把任何一个点作为树根,所以不妨假设结点1为整棵树的根即可。也正因此,建图时需要建双向边,在做dp时也要加入相应标记。
② 上述思路可以避免一个特殊情况的判断,看这组数据:
2
-3 -4
1 2
这里所有结点的value都是负值,但是题目要求的是非空子集,所以答案应该是-3,而不是0(不可以一个点都不选)
这么水的题为何我前两天跪了10发?搞不懂。。。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
const int MAX = 16007;
vector<int> G[MAX];
int dp[MAX];
int val[MAX];
bool vis[MAX];
int n;
void kiss(int root) {
vis[root] = true;
for (int i = 0; i < G[root].size(); ++i) {
if (!vis[G[root][i]]) {
kiss(G[root][i]);
if (dp[G[root][i]] > 0) dp[root] += dp[G[root][i]];
}
}
}
int main() {
scanf(" %d", &n);
for (int i = 1; i <= n; ++i) {
scanf(" %d", val + i);
dp[i] = val[i];
G[i].clear();
}
int a, b;
for (int i = 1; i < n; ++i) {
scanf(" %d %d", &a, &b);
G[a].push_back(b);
G[b].push_back(a);
}
memset(vis, false, sizeof(vis));
kiss(1); // kiss root
int ans = -0xfffffff;
for (int i = 1; i <= n; ++i) {
if (dp[i] > ans) ans = dp[i];
}
printf("%d\n", ans);
return 0;
}