题目大意:
0~n-1号这n个点,每个点有个权值,由无向边形成了一棵树,希望在这棵树上找到一棵长为m的子树使总的权值最小
基本的树形背包问题
令dp[u][j] 表示u号节点对应子树中有j个节点所能得到的最大权值
dp[u][1] = val[u]
dp[u][j] = max{dp[v][k] + dp[u][j-k]} j>1 1=<k<j
我们通过dfs自底向上更新
在dfs过程中建立一个 j ,k 的循环即可
我一开始想的时候认为这个只做到了 u 的下方考虑,如果所得到的子树是要往u的上方走怎么办,想着自顶向上多一次dfs,然后就再也想不出来了- -
其实后来想想却发现如果要往上走,那么就得到的是以走到最上方的那个节点所形成的子树,那个值也已经在dp数组中保存了,不用我多去考虑了
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5 6 const int N = 105; 7 int first[N] , k , val[N] , dp[N][N]; 8 9 struct Edge{ 10 int y , next; 11 }e[N<<1]; 12 13 void add_edge(int x , int y) 14 { 15 e[k].y = y , e[k].next = first[x]; 16 first[x] = k++; 17 } 18 19 void dfs(int u , int fa , int m) 20 { 21 dp[u][1] = val[u]; 22 for(int i = first[u] ; i!=-1 ; i=e[i].next) 23 { 24 int v = e[i].y; 25 if(v == fa) continue; 26 dfs(v , u , m); 27 for(int j = m ; j>=2 ; j--){ 28 for(int k=1 ; k<j ; k++) 29 dp[u][j] = max(dp[u][j] , dp[v][k] + dp[u][j-k]); 30 } 31 } 32 } 33 34 int main() 35 { 36 // freopen("a.in" , "r" , stdin); 37 int n , m , x , y; 38 while(scanf("%d%d" , &n , &m)==2){ 39 for(int i =0 ; i<n ; i++) 40 scanf("%d" , val+i); 41 42 memset(first , -1 , sizeof(first)); 43 k=0; 44 for(int i=1 ; i<n ; i++){ 45 scanf("%d%d" , &x , &y); 46 add_edge(x , y); 47 add_edge(y , x); 48 } 49 50 memset(dp , 0 , sizeof(dp)); 51 dfs(0 , -1 , m); 52 53 int maxn = 0; 54 for(int i=0 ; i<n ; i++){ 55 maxn = max(maxn , dp[i][m]); 56 } 57 printf("%d " , maxn); 58 } 59 return 0; 60 }