题目大意:给出一棵N个点的无根树,每个节点都有对应的值
现要求你找出一棵K个节点的子树,使得这棵子树上的值的和最大
解题思路:无根树先化为有根树,以0为根
K个节点的子树,K个节点里面的任意一个节点都可以为根,所以就不用考虑哪个节点是这棵子树的根的问题了,只需要由根0向下递归寻找就可以了
用dp[i][j]表示以i为根节点且节点数为j的树的和的最大值
那么dp[i][j] = max(dp[i][j], dp[i][j-k] + dp[son][k]) son是i的子节点
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define maxn 110
vector<int> Node[maxn];
int w[maxn], dp[maxn][maxn], son[maxn];
int n, k, ans;
int dfs(int cur, int fa) {
int size = Node[cur].size();
son[cur] = 1;
for(int i = 0; i < size; i++) {
if(Node[cur][i] != fa) {
son[cur] += dfs(Node[cur][i], cur);
}
}
dp[cur][1] = w[cur];
for(int i = 0; i < size; i++) {
int u = Node[cur][i];
if(u == fa)
continue;
for(int j = son[cur]; j >= 1; j--)
for(int l = 0; l < j && l <= son[u]; l++)
dp[cur][j] = max(dp[cur][j], dp[cur][j-l] + dp[u][l]);
}
if(dp[cur][k] > ans)
ans = dp[cur][k];
return son[cur];
}
int main() {
while(scanf("%d%d", &n, &k) != EOF) {
for(int i = 0; i < n; i++) {
Node[i].clear();
scanf("%d", &w[i]);
}
int x, y;
for(int i = 0; i < n - 1; i++) {
scanf("%d%d", &x, &y);
Node[x].push_back(y);
Node[y].push_back(x);
}
ans = 0;
memset(dp,0,sizeof(dp));
dfs(0,-1);
printf("%d\n",ans);
}
return 0;
}