二叉苹果树()-其他
二叉苹果树()
二叉苹果树
有一棵二叉苹果树,如果树枝有分叉,一定是分两叉,即没有只有一个儿子的节点。
这棵树共 $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/