dp[u][j] 表示 u 子树内保留 j 条边最多留住多少苹果。
P2015 二叉苹果树
GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。
一、先回答问题
只贪心保留苹果最多的 Q 条边,会破坏必须与根连通的限制。
二、核心观察
每个孩子子树是一组选择,分配 k 条边给它时还要加上连接孩子的 1 条边。
为什么不丢答案:子树之间只通过 u 相连,保留边数可以独立分配后做背包合并。
dp[u][j]
孩子 v
分配 k
三、互动演示
步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推
点击下一步开始。
四、自己选答案
为什么要 +1 条边?
先判断,再点按钮。
j 为什么倒序?
先判断,再点按钮。
五、手推结果
| 转移项 | 意义 |
|---|---|
| dp[u][j-k-1] | 留给已处理部分 |
| dp[v][k] | 孩子子树 |
| +w | 连接孩子的边 |
课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。
六、C++14 参考代码
#include <bits/stdc++.h>
using namespace std;
int n, Q, dp[105][105];
vector<pair<int,int> > g[105]; // {孩子, 边权}
void dfs(int u, int fa) {
for (size_t i = 0; i < g[u].size(); i++) {
int v = g[u][i].first, w = g[u][i].second;
if (v == fa) continue;
dfs(v, u);
for (int j = Q; j >= 1; j--) // 当前总边数,从大到小
for (int k = 0; k <= j - 1; k++) // 分给 v 子树 k 条 + 连 v 的边
dp[u][j] = max(dp[u][j], dp[u][j - k - 1] + dp[v][k] + w);
}
}
int main() {
cin >> n >> Q;
for (int i = 1; i < n; i++) {
int a, b, c; cin >> a >> b >> c;
g[a].push_back(make_pair(b, c));
g[b].push_back(make_pair(a, c));
}
dfs(1, 0);
cout << dp[1][Q] << "\n";
return 0;
}