【树上背包】洛谷P2014 [CTSC1997] 选课

发布时间 2023-08-05 21:26:58作者: blockche

【树上背包】洛谷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;
}