二叉苹果树()

二叉苹果树

有一棵二叉苹果树,如果树枝有分叉,一定是分两叉,即没有只有一个儿子的节点。

这棵树共 $N$ 个节点,编号为 $1$ 至 $N$,树根编号一定为 $1$。

我们用一根树枝两端连接的节点编号描述一根树枝的位置。

一棵苹果树的树枝太多了,需要剪枝。但是一些树枝上长有苹果,给定需要保留的树枝数量,求最多能留住多少苹果。

这里的保留是指最终与 $1$ 号点连通。

输入格式

第一行包含两个整数 $N$ 和 $Q$,分别表示树的节点数以及要保留的树枝数量。

接下来 $N1$ 行描述树枝信息,每行三个整数,前两个是它连接的节点的编号,第三个数是这根树枝上苹果数量。

输出格式

输出仅一行,表示最多能留住的苹果的数量。

数据范围

$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/

————————

二叉苹果树

有一棵二叉苹果树,如果树枝有分叉,一定是分两叉,即没有只有一个儿子的节点。

这棵树共 $N$ 个节点,编号为 $1$ 至 $N$,树根编号一定为 $1$。

我们用一根树枝两端连接的节点编号描述一根树枝的位置。

一棵苹果树的树枝太多了,需要剪枝。但是一些树枝上长有苹果,给定需要保留的树枝数量,求最多能留住多少苹果。

这里的保留是指最终与 $1$ 号点连通。

输入格式

第一行包含两个整数 $N$ 和 $Q$,分别表示树的节点数以及要保留的树枝数量。

接下来 $N1$ 行描述树枝信息,每行三个整数,前两个是它连接的节点的编号,第三个数是这根树枝上苹果数量。

输出格式

输出仅一行,表示最多能留住的苹果的数量。

数据范围

$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/