二叉苹果树
二叉苹果树
有一棵二叉苹果树,如果树枝有分叉,一定是分两叉,即没有只有一个儿子的节点。
这棵树共 $N$ 个节点,编号为 $1$ 至 $N$,树根编号一定为 $1$。
我们用一根树枝两端连接的节点编号描述一根树枝的位置。
一棵苹果树的树枝太多了,需要剪枝。但是一些树枝上长有苹果,给定需要保留的树枝数量,求最多能留住多少苹果。
这里的保留是指最终与 $1$ 号点连通。
输入格式
第一行包含两个整数 $N$ 和 $Q$,分别表示树的节点数以及要保留的树枝数量。
接下来 $N−1$ 行描述树枝信息,每行三个整数,前两个是它连接的节点的编号,第三个数是这根树枝上苹果数量。
输出格式
输出仅一行,表示最多能留住的苹果的数量。
数据范围
$1 \leq Q < N \leq 100$.
$N \ne 1$,
每根树枝上苹果不超过 $30000$ 个。
输入样例:
5 2 1 3 1 1 4 10 2 3 20 3 5 20
输出样例:
21
解题思路
这是有依赖的背包问题的简化版。因为选择的树枝要和根节点连通,因此可以把每个节点看成是物品,如果要选择这个物品就一定要选择这个物品的根节点,因此就存在一个依赖关系。把每个边的价值转变到节点的价值,然后每个节点的体积都为$1$,就可以转化成有依赖的背包问题。
用$f(i,j)$来表示在以$i$为根的子树中选择$j$条边的最大价值。把以$i$为节点的树的每一个子节点看成是各组物品,再根据各个物品组选择不同的边数进行状态转移。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 110, M = N * 2; 5 6 int n, m; 7 int head[N], e[M], wts[M], ne[M], idx; 8 int f[N][N]; 9 10 void add(int v, int w, int wt) { 11 e[idx] = w, wts[idx] = wt, ne[idx] = head[v], head[v] = idx++; 12 } 13 14 void dfs(int u, int pre) { 15 for (int i = head[u]; i != -1; i = ne[i]) { 16 if (e[i] != pre) { 17 dfs(e[i], u); 18 for (int j = m; j; j--) { 19 for (int k = 0; k < j; k++) { 20 f[u][j] = max(f[u][j], f[u][j - k - 1] + f[e[i]][k] + wts[i]); 21 } 22 } 23 } 24 } 25 } 26 27 int main() { 28 scanf("%d %d", &n, &m); 29 30 memset(head, -1, sizeof(head)); 31 for (int i = 0; i < n - 1; i++) { 32 int v, w, wt; 33 scanf("%d %d %d", &v, &w, &wt); 34 add(v, w, wt), add(w, v, wt); 35 } 36 37 dfs(1, -1); 38 printf("%d", f[1][m]); 39 40 return 0; 41 }
参考资料
AcWing 1074. 二叉苹果树(算法提高课):https://www.acwing.com/video/416/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16552605.html