【树上背包】洛谷P2014 [CTSC1997] 选课
【树上背包】洛谷P2014 [CTSC1997] 选课
题目链接:[P2014 CTSC1997] 选课 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 \(N\) 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程 a 是课程 b 的先修课即只有学完了课程 a,才能学习课程 b)。一个学生要从这些课程里选择 \(M\) 门课程学习,问他能获得的最大学分是多少?
题解
简单的树上背包模板题,定义\(dp[u][i]\)为点u已经选了i门课获得的最大学分,dfs递归处理,具体看代码。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<int> s(n + 1);
vector<vector<int>> adj(n + 1);
for (int i = 1; i <= n; i++) {
int pa;
cin >> pa >> s[i];
adj[pa].push_back(i);
}
auto dfs = [&](auto self, int u) -> vector<int> {
vector<int> ans(2);
ans[1] = s[u];
for (auto v : adj[u]) {
vector<int> f = self(self, v);
int n1 = ans.size(), n2 = f.size();
ans.resize(ans.size() + f.size());
for (int i = n1 - 1; i >= 1; i--) { // 从大到小,避免重复取;i不能等于0,因为当前节点u必须要选。
for (int j = 0; j < n2; j++) {
ans[i + j] = max(ans[i + j], ans[i] + f[j]);
}
}
}
return ans;
};
auto dp = dfs(dfs, 0);
cout << dp[m + 1] << '\n'; // 因为把0点也加入其中了,所以是最终选了m + 1门课
return 0;
}