简单Tree DP. 求一个含有k个节点的子树,使得子树上所有节点的权值和最大。方程:dp[i][j] 表示以i为根的子树中选取j个点所能获得最大值,其中i点必须选,然后就是直接使用分组背包就可以了。
/*
*author : csuchenan
*Prog : ZOJ 3201
*Algorithm : Tree DP dp[i][j] 以i为根
* 的子树中选取j个点的最大值,i点必须选
*Accepted 3201 C++ 0ms 224KB chenan
*/
#include <cstdio>
#include <cstring>
#include <vector>
using std::vector ;
#define maxn 105
int num[maxn] ;
int dp[maxn][maxn] ;
vector<int> G[maxn] ;
int n , K ;
void init(){
for(int i = 0 ; i <= n ; i ++){
G[i].clear() ;
}
memset(dp , 0 , sizeof(dp)) ;
}
bool read(){
if(scanf("%d%d" , &n , &K) == EOF)
return 0 ;
init() ;
for(int i = 0 ; i < n ; i ++)
scanf("%d" , &num[i]) ;
int p , q ;
for(int i = 1 ; i < n ; i ++){
scanf("%d%d" , &p , &q) ;
G[p].push_back(q) ;
G[q].push_back(p) ;
}
return 1 ;
}
int dfs(int v , int f){
int cnt = 1 ;
for(int i = 0 ; i != G[v].size() ; i ++){
int u = G[v][i] ;
if(u == f)
continue ;
cnt += dfs(u , v) ;
}
int tmp = cnt > K ? K : cnt ;
for(int i = 0 ; i != G[v].size() ; i ++){
int u = G[v][i] ;
if(u == f)
continue ;
for(int j = tmp ; j >= 1 ; j --){
for(int t = 1 ; t < j ; t ++){
if(dp[v][j] < dp[v][j - t] + dp[u][t]){
dp[v][j] = dp[v][j - t] + dp[u][t] ;
}
}
}
}
for(int t = 1 ; t <= tmp ; t ++){
dp[v][t] += num[v] ;
}
return cnt ;
}
void solve(){
int ans = 0 ;
dfs(0 , -1) ;
for(int i = 0 ; i < n ; i ++){
ans = ans > dp[i][K] ? ans : dp[i][K] ;
}
printf("%d\n" , ans);
}
int main(){
while(read()){
solve() ;
}
return 0 ;
}