题目描述
有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)
这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树
2 5 / 3 4 / 1 现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
给定需要保留的树枝数量,求出最多能留住多少苹果。
输入输出格式
输入格式:第1行2个数,N和Q(1<=Q<= N,1<N<=100)。
N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。
每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。
每根树枝上的苹果不超过30000个。
输出格式:一个数,最多能留住的苹果的数量。
输入输出样例
分析
>:<自己做出来的第一道树形动规题,纪念!!!
树形动规,+背包
如果想要保留下树枝a,那么首先要留下a的父亲的树枝。
所以:dp[u][j]以u为根的子树中留下了j条(一定是联通的),的最大价值。
那么从叶推到根即可。
转移方程:dp[u][j] = max(dp[u][j],dp[v][k]+dp[u][j-k-1]+w);
就是从子树v中选取k条树枝,从u树其他的子树中选取j-k-1条,再加上连接v的那一条 合起来 就是从树u中选j条边的最大价值。
code
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 5 using namespace std; 6 7 const int N = 110; 8 9 struct Edge{ 10 int to,nxt,w; 11 }e[10010]; 12 int head[10010],tot; 13 14 int dp[N][N]; 15 int Sum,n,m; 16 17 inline int read() { 18 int x = 0,f = 1;char ch = getchar (); 19 for (; ch<'0'||ch>'9'; ch = getchar()) 20 if (ch=='-') f = -1; 21 for (; ch>='0'&ch<='9'; ch = getchar()) 22 x = x*10+ch-'0'; 23 return x*f; 24 } 25 26 inline void add_edge(int u,int v,int w) { 27 e[++tot].to = v,e[tot].w = w,e[tot].nxt = head[u],head[u] = tot; 28 } 29 30 void dfs(int u,int fa) { 31 for (int i=head[u]; i; i=e[i].nxt) { 32 int v = e[i].to,w = e[i].w; 33 if (v==fa) continue; 34 dfs(v,u); 35 for (int j=m; j>=1; --j) 36 for (int k=0; k<j; ++k) 37 dp[u][j] = max(dp[u][j],dp[v][k]+dp[u][j-k-1]+w); 38 } 39 } 40 int main() { 41 42 n = read(),m = read(); 43 for (int a,b,c,i=1; i<n; ++i) { 44 a = read(),b = read(),c = read(); 45 add_edge(a,b,c),add_edge(b,a,c); 46 } 47 48 dfs(1,0); 49 printf("%d",dp[1][m]); 50 51 return 0; 52 }