题目描述
#10153. 「一本通 5.2 例 1」二叉苹果树 - 题目 - LibreOJ (loj.ac) 有一棵二叉苹果树,如果树枝有分叉,一定是分两叉,即没有只有一个儿子的节点。这棵树共 个节点,标号 至 ,树根编号一定为 。
我们用一根树枝两端连接的节点编号描述一根树枝的位置。一棵有四根树枝的苹果树,因为树枝太多了,需要剪枝。但是一些树枝上长有苹果,给定需要保留的树枝数量,求最多能留住多少苹果。
输入格式
第一行两个数 和 , 表示树的节点数, 表示要保留的树枝数量。
接下来 行描述树枝信息,每行三个整数,前两个是它连接的节点的编号,第三个数是这根树枝上苹果数量。
输出格式
输出仅一行,表示最多能留住的苹果的数量。
案例
input
5 2
1 3 1
1 4 10
2 3 20
3 5 20
output
21
数据范围
对于 的数据,,每根树枝上苹果不超过 个。
思路
需要在树中找到包含根节点且长度为Q的一条路径, 其包含的苹果数量最大
且根据剪枝的性质, 如果当前树枝需要保留, 那么其父节点的树枝也一定需要保留, 也就是说存在依赖性。
把树枝权重放到点上, 树枝数量当做背包容量, 每个物品重量为1, 可以转化为背包问题中的有依赖背包问题。复杂度为 可以通过。
状态表示:f[i][j]
以i为根的节点的子树中选择j个树枝
对于i根下的子节点 S1, S2, 把每个子节点根据分配的可选树枝数量划分
即S1可以划分为:
S2可以划分为:
因此在求
f[i][j]
时, 可以从子树中的所有状态转移过来
需要三层循环, 第一次i循环子节点, 第二次j循环当前i节点的体积, 第三次k循环分配给子节点的体积。
代码
const int N = 110, M = N * 2;
int h[N], e[M], ne[M], w[M], idx;
int n, Q;
int f[N][N];
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
void dfs(int u, int fa)
{
for (int i = h[u]; ~i; i = ne[i])
{
int t = e[i];
if (t == fa)
continue;
dfs(t, u);
for (int j = Q; j >= 0; j--)
for (int k = 0; k < j; k++)
f[u][j] = max(f[u][j], f[t][k] + f[u][j - k - 1] + w[i]);
}
}
int main()
{
cin >> n >> Q;
memset(h, -1, sizeof h);
for (int i = 1; i < n; i++)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
dfs(1, -1);
cout << f[1][Q] << endl;
return 0;
}