题目链接:
题意:
有一棵树,每个节点有一个权值,给定一个值k,求大小为k的子树中每个节点权值和最大为多少。
思路:
树规方程:f(i,j)=max{ f(i,j-p)+f(v,p) | 1<p<j 且 v是i的子节点 }
f(i,j):表示以i为根节点,大小为 j 的子树的最大权值。
siz[i]:表示以 i 为根节点的子树大小。
以每个节点为根节点,对其子树做背包。注意对于根节点为i,大小为 j 的子树来说,j 要从 siz[i] -> 2,若从小到大的话某些节点的权值会被重复计算。(即01背包原理)
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 100 + 10;
int n, k;
int w[MAX];
vector<int>mp[MAX];
int siz[MAX];
int dp[MAX][MAX];
int ans;
void dfs(int root, int fa)
{
siz[root] = 1;
for (int i = 0; i < mp[root].size(); i++) {
int v = mp[root][i];
if (v == fa) continue;
dfs(v, root);
siz[root] += siz[v];
}
dp[root][1] = w[root];
for (int p = 0; p < mp[root].size(); p++) {
int v = mp[root][p];
if (v == fa) continue;
for (int i = siz[root]; i > 1; i--) {
for (int j = 1; j < i&&j <= siz[v]; j++) {
dp[root][i] = max(dp[root][i], dp[root][i - j] + dp[v][j]);
}
}
}
if (siz[root] >= k) {
ans = max(ans, dp[root][k]);
}
}
int main()
{
while (scanf("%d%d", &n, &k) != EOF)
{
memset(siz, 0, sizeof(siz));
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; i++) {
scanf("%d", &w[i]);
mp[i].clear();
}
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
mp[u].push_back(v);
mp[v].push_back(u);
}
ans = 0;
dfs(0, -1);
printf("%d\n", ans);
}
return 0;
}