某研究院有
题目
科研团队组建
给定一棵有根树(根为 1 号节点),每个节点有一个能力值(可正可负)。需要恰好选择
任务要求
- 从标准输入读入树的结构、节点能力值和团队人数
; - 使用树形背包动态规划求解;
- 输出最大能力值总和。
输入格式
- 第一行:两个整数
和 ( ); - 接下来
行,每行两个整数 ,表示 是 的子节点(即 是 的上级); - 最后一行:
个整数,第 个整数表示节点 的能力值 。
输出格式
- 输出一个整数,表示满足条件的
人团队的最大能力值总和。 - 若无法组建(如
小于最小必需人数),输出IMPOSSIBLE。
数据范围与限制
| 项目 | 范围 |
|---|---|
| 节点数 | |
| 团队人数 | |
| 能力值 | |
| 时间复杂度要求 | |
| 额外空间限制 |
关于根节点
树以节点 1 为根。根节点 1 若被选中,不需要满足额外的父节点约束。若根节点 1 的能力值为负,可以选择不包含它——但此时它的子节点都无法被选中。设计 DP 状态时需要考虑这一边界情况。
样例
样例输入 1
5 3
1 2
1 3
2 4
2 5
10 5 3 2 1样例输出 1
18样例解释 1
树结构:
1(10)
/ \
2(5) 3(3)
/ \
4(2) 5(1)选节点
选节点
选节点
选节点
最大值为 18。
样例输入 2
3 2
1 2
1 3
-5 3 4样例输出 2
2样例解释 2
根节点 1 能力值为
若不选 1,则无法选任何节点(依赖约束)。
但题目要求恰好选
实际上应该选1和2:-5+3=-2,或1和3:-5+4=-1。最大值是-1。但样例输出是2...
让我重新设计样例2。
样例输入 2(修正)
3 2
1 2
1 3
5 -1 -2样例输出 2(修正)
4样例解释 2(修正)
选节点
选节点
最大值为 4。
如何验证
先安装 Node.js、pnpm 和支持 C++17 的编译器。GNU Make 是首选入口,但不是强制依赖。
# 已进入本 Lab 目录
make doctor
make run
make run CASE=001-sample
make interactive
make scoreWindows 没有安装 Make 时,在仓库根目录使用完全相同的评分内核:
pnpm lab:doctor -- labs/chapter-04/exercise/E-04-17-research-team-formation
pnpm lab:run -- labs/chapter-04/exercise/E-04-17-research-team-formation
pnpm lab:run -- labs/chapter-04/exercise/E-04-17-research-team-formation --case 001-sample
pnpm lab:score -- labs/chapter-04/exercise/E-04-17-research-team-formationmake run 在答案尚未全对时仍正常返回,避免 Make 把学习结果显示成工具故障;make score 是严格入口,只有 100 分才返回成功。标准输出参与判题,调试信息请写入标准错误。
题解
点击查看题解
思路
使用树形背包(Tree Knapsack)。定义状态:
初始化:
转移:逐个合并子树
注意枚举顺序:
最终答案:由于根节点 1 必须选才能选其他节点,答案为 IMPOSSIBLE。
算法步骤
- 建树,以节点 1 为根;
- 后序遍历(DFS),对每个节点
:- 初始化
,其余为 ; - 对每个子节点
,先递归计算 ; - 将
合并到 :倒序枚举 ,正序枚举 ,更新 ;
- 初始化
- 输出
(若为 则输出IMPOSSIBLE)。
复杂度分析
- 时间复杂度:
。每对节点 的合并需要 ,共 条边。 - 空间复杂度:
,存储 DP 数组。
边界注意
- 能力值可能为负,初始化不能用 0,要用
; 时只能选根节点;- 若根节点价值为负且
,根仍然必须选(否则无法选其他节点)。
点击查看参考代码
#include <bits/stdc++.h>
using namespace std;
const int INF = -1e9;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
vector<vector<int>> adj(n + 1);
for (int i = 0; i < n - 1; ++i) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
}
vector<int> val(n + 1);
for (int i = 1; i <= n; ++i) cin >> val[i];
vector<vector<int>> dp(n + 1, vector<int>(k + 1, INF));
function<void(int)> dfs = [&](int%20u) {
dp[u][1] = val[u];
for (int v : adj[u]) {
dfs(v);
for (int j = k; j >= 1; --j) {
if (dp[u][j] == INF) continue;
for (int t = 1; t <= k - j; ++t) {
if (dp[v][t] == INF) continue;
dp[u][j + t] = max(dp[u][j + t], dp[u][j] + dp[v][t]);
}
}
}
};
dfs(1);
if (dp[1][k] == INF) {
cout << "IMPOSSIBLE\n";
} else {
cout << dp[1][k] << '\n';
}
return 0;
}