继续将树形DP题目给与整理。这个题目能很容易想到状态,dp[i][j]表示以i为根的子树中走j步所能获得的最大值。但是这样状态转移的时候比较难写,而状态转移比较麻烦的原因在于不能确定走到了哪一步。我们增加一维,dp[i][j][2],其中dp[i][j][0]表示要回到根节点,dp[i][j][1]表示不回到根节点。这样我们就很容易能得到状态转移方程了,很容易推出来,具体看程序。
/*
*author : csuchenan
*PROG : POJ2486
*Algorithm : Tree DP dp[i][j][0]代表从i出发走j步回到i点
dp[i][j][1]代表从i出发走j步不回到i点
*notice : 很容易想到用dp[i][j]来表示从i出发走j步得到
* 最大值,需要注意当状态不够时可以通过曾加维
* 数来保存额外的信息。
*csuchenan 2486 Accepted 360K 63MS C++ 1738B
*/
#include <cstdio>
#include <cstring>
#include <vector>
using std::vector ;
#define maxn 105
vector<int> G[maxn] ;
int val[maxn] ;
int dp[maxn][maxn * 2][2] ;
int n , k ;
void init(){
for(int i = 1 ; i <= n ; i ++){
G[i].clear() ;
}
memset(dp , 0 , sizeof(dp)) ;
}
bool read(){
if(scanf("%d%d" , &n , &k) == EOF )
return 0 ;
init() ;
int p , q ;
for(int i = 1 ; i <= n ; i ++){
scanf("%d" , &val[i]) ;
}
for(int i = 1 ; i < n ; i ++){
scanf("%d%d" , &p , &q) ;
G[p].push_back(q) ;
G[q].push_back(p) ;
}
return 1 ;
}
inline int max(int x , int y){
return x > y ? x : y ;
}
void dfs(int v , int f){
for(int i = 0 ; i != G[v].size() ; i ++){
int u = G[v][i] ;
if(u == f)
continue ;
dfs(u , v) ;
}
for(int i = 0 ; i != G[v].size() ; i ++){
int u = G[v][i] ;
if(u == f)
continue ;
for(int s = k ; s >= 0 ; s --){
for(int j = 0 ; j < s ; j ++){
if(s - j - 2 >= 0)
dp[v][s][0] = max(dp[v][s][0] , dp[u][j][0] + dp[v][s - j - 2][0]) ;
if(s - j - 2 >= 0)
dp[v][s][1] = max(dp[v][s][1] , dp[u][j][0] + dp[v][s - j - 2][1]) ;
dp[v][s][1] = max(dp[v][s][1] , dp[u][j][1] + dp[v][s - j - 1][0]) ;
}
}
}
for(int i = 0 ; i <= k ; i ++){
dp[v][i][0] += val[v] ;
dp[v][i][1] += val[v] ;
}
}
void solve(){
dfs(1 , 0) ;
printf("%d\n" , max(dp[1][k][0] , dp[1][k][1]) ) ;
}
int main(){
while(read()){
solve() ;
}
return 0 ;
}