ZOJ 3201 树形背包问题

题目大意:

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 }
原文地址:https://www.cnblogs.com/CSU3901130321/p/4233925.html