😊😊 😊😊
不求点赞,只求耐心看完,指出您的疑惑和写的不好的地方,谢谢您。本人会及时更正感谢。希望看完后能帮助您理解算法的本质
😊😊 😊😊
题目描述:
一、本题考察算法:树形DP😊
思路:
- 由题意可知,父子节点之间相连,那么选了父节点,则该父亲的所有子节点都不能选。直接相连的两个节点只能选其中一个.
- 对于当前节点是选还是不选,取决于它的左右子树状况,有时候,父节点的权值大于子树节点的权值之和,此时应该选父节点。但是有时父节点的权值小于子树节点的权值之和,此时不选当前节点。
- 所以由上述分析我们可以设: f [ u ] [ 0 / 1 ] f[u][0/1] f[u][0/1]:表示以u为根的子树,不选或者选择 u u u 号节点的最大权值。目标求值: m a x ( f [ u ] [ 1 ] , f [ u ] [ 0 ] ) ; max(f[u][1], f[u][0]); max(f[u][1],f[u][0]);
- 计算
f
[
u
]
[
1
/
0
]
f[u][1/0]
f[u][1/0]:对于该方程而言,
选择父节点的话:则选择根节点的最大权值为:
f [ u ] [ 1 ] = ∑ u = 1 s o n f [ i ] [ 0 ] , i 是儿子节点的编号。 f[u][1] = \sum\limits_{u=1}^{son}f[i][0],\ i是儿子节点的编号。 f[u][1]=u=1∑sonf[i][0], i是儿子节点的编号。
不选择父节点 u u u:则根节点的最大权值为:
f [ u ] [ 0 ] = ∑ i = 1 s o n m a x ( f [ i ] [ 1 ] , f [ i ] [ 0 ] ) ; f[u][0] = \sum\limits_{i=1}^{son}max(f[i][1], f[i][0]); f[u][0]=i=1∑sonmax(f[i][1],f[i][0]);
又因为是在树上,所以对于子树的权值和,同样的计算方式,只不过采用的递归回溯去求解!
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 6e3 + 10;
int h[N], e[N], ne[N], idx;
int n;
int happy[N];
bool fa[N];
int f[N][2]; //第一维表示节点编号 。第二维表示节点u选还是不选的最大价值
void add (int a, int b) //a是b的父节点!
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
//当前子树的根节点:对于第u个节点选还是不选取决于它子树的影响状态:所以需要递归到底! 由底部节点决定上一层选还是不选,比较选和不选的最大价值。
void dfs (int u)
{
f[u][1] = happy[u]; //选第u个节点的话 ,其价值就是该节点的价值 + 子树的价值(该节点的儿子节点不算,算它的儿子节点的子树)
for (int i=h[u]; ~i; i = ne[i])
{
int v = e[i];
dfs (v); //递归到底部!
f[u][0] += max(f[v][1], f[v][0]); //父节点 累加子节点!所以是+=
f[u][1] += f[v][0];
}
}
int main()
{
cin >> n;
memset(h, -1, sizeof (h));
for (int i=1; i <= n; i ++)
cin >> happy[i];
for (int i=1; i < n; i ++)
{
int a, b;
cin >> a >> b; //b是a的父节点!注意这是有向图!而不是无向图!
add (b, a);
fa[a] = true; //表示a号节点有父节点!
}
int root = 1;
while (fa[root]) root ++; //此时root是根节点!
dfs (root);
cout << max(f[root][1], f[root][0]) << endl;
return 0;
}