问题描述
有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?
解题思路
很简单的一道入门树状dp,把求最终解分成两种情况
- 根结点被选中,则所有的子节点一定不能被选中
- 根节点没被选中,则子节点可被选中,注意是可以被选中,有的最优解中很可能不选择某一个子节点而选择子节点的子节点。
设 d(r,s) 为r顶点在选中或为选中时的该树的最大权值和,则可列出状态转移方程
d(r,1)=v(r)+∑d(ci,0)
d(r,0)=∑max{d(ci,1),d(ci,0)}
则最终的结果就是选根节点两种情况中最大的一个
#include <iostream>
#include <cstring>
#define N 100000
#define MAX(x, y) ((x)>(y)?(x):(y))
struct edge
{
int to;
int next;
};
int tree[N];
int weight[N];
edge edges[2*N];
int d[N][2];
int len = 0;
void add(int x, int y)
{
edges[len].to = y;
edges[len].next = tree[x];
tree[x] = len++;
}
void dp(int root, int p)
{
if(tree[root] == -1)
{
d[root][0] = 0;
d[root][1] = weight[root];
return;
}
for(int i = tree[root]; i != -1; i = edges[i].next)
{
int child = edges[i].to;
if(child == p)
continue;
if(!d[child][1])
dp(child, root);
d[root][0] += MAX(d[child][1], d[child][0]);
d[root][1] += d[child][0];
}
d[root][1] += weight[root];
}
int main()
{
memset(tree, -1, sizeof(tree));
int n;
std::cin >> n;
for(int i = 1; i <= n; i++)
std::cin >> weight[i];
int x, y;
for(int i = 0; i < n-1; i++)
{
std::cin >> x >> y;
add(x, y);
add(y, x);
}
dp(1, -1);
int max = MAX(d[1][0], d[1][1]);
std::cout << max << std::endl;
}