题目描述:
有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?
n <= 100000,权值均为不超过1000的正整数。
题解:
因为一个点有被选择和不被选择两个状态,所以设计状态 dp[x][z] 表示以点 x 为子树且点 x 在 z 状态下的最大值,z = 0表示不选点 x,z = 1表示选择点 x,状态转移方程为:
d
p
[
x
]
[
0
]
=
∑
y
(
x
,
y
)
∈
E
,
y
≠
p
a
r
e
n
t
[
x
]
m
a
x
(
d
p
[
y
]
[
1
]
,
d
p
[
y
]
[
0
]
)
d
p
[
x
]
[
1
]
=
w
[
x
]
+
∑
y
(
x
,
y
)
∈
E
,
y
≠
p
a
r
e
n
t
[
x
]
d
p
[
y
]
[
0
]
dp[x][0] = \sum_y^{(x,y)∈E,y≠parent[x]}{max(dp[y][1],dp[y][0])}\\ dp[x][1] = w[x]+\sum_y^{(x,y)∈E,y≠parent[x]}{dp[y][0]}
dp[x][0]=y∑(x,y)∈E,y=parent[x]max(dp[y][1],dp[y][0])dp[x][1]=w[x]+y∑(x,y)∈E,y=parent[x]dp[y][0]
我们以 1 为树的根进行dfs,最终 max(dp[1][0], dp[1][1]) 即为解。
注意:当图为双向边且使用链式前向星时,数组别忘了开两倍!
AC Codes:
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
//#include <unordered_set>
//#include <unordered_map>
#include <deque>
#include <list>
#include <iomanip>
#include <algorithm>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
//#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
//cout << fixed << setprecision(2);
//cout << setw(2);
const int N = 1e5 + 6, M = 1e9 + 7;
int head[N], Next[N << 1], ver[N << 1], parent[N], tot;
int w[N], dp[N][2];
void add(int u, int v) {
ver[++tot] = v;
Next[tot] = head[u], head[u] = tot;
}
void dfs(int x) {
dp[x][1] = w[x];
for (int i = head[x]; i; i = Next[i]) {
int y = ver[i];
if (y == parent[x]) continue;
parent[y] = x;
dfs(y);
dp[x][1] += dp[y][0], dp[x][0] += max(dp[y][0], dp[y][1]);
}
}
int main() {
//freopen("/Users/xumingfei/Desktop/ACM/test.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
tot = 1;
for (int i = 1; i <= n; i++) cin >> w[i];
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
add(x, y), add(y, x);
}
parent[1] = 0;
dfs(1);
cout << max(dp[1][0], dp[1][1]) << '\n';
return 0;
}