洛谷P2014 选课 题解 树形DP

题目链接:https://www.luogu.com.cn/problem/P2014

题目大意:

大学里有 \(n\) 门课,这些课之间有依赖关系(先修课)。每门课有学分 \(s[i]\) ,求选择 \(m\) 课最多能获得的学分?

解题思路:

树形DP。

我们可以假设先修课和当前这节课之间是父子关系(当前课是儿子,先修课是父亲),然后没有先修课的节点的父节点为 \(0\) 号节点,这样就构成了一棵以 \(0\) 为根节点的树。

然后定义状态 \(f[u][i]\) 表示以 \(u\) 为根节点的子树中选择了 \(i\) 门课的最大学分。
其实这个状态的具体含义是:

\(u\) 为根节点,(并且此时遍历到了它的子节点 \(v\)),到子节点 \(v\) 为止(也就是从第 \(1\) 个儿子到子节点 \(v\))选了 \(i\) 门课的最大学分。

\(f[u][i] = max(f[u][i-j] + f[v][j])\)

这其中蕴含着一定的 背包 思想。

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 330;
int n, m, p, s[maxn], f[maxn][maxn], size[maxn];
vector<int> g[maxn];
void dfs(int u) {
    f[u][1] = s[u];
    size[u] = 1;
    int sz = g[u].size();
    for (int i = 0; i < sz; i ++) {
        int v = g[u][i];
        dfs(v);
        size[u] += size[v];
    }
    for (vector<int>::iterator it = g[u].begin(); it != g[u].end(); it ++) {
        int v = *it;
        for (int i = size[u]; i >= 2; i --) {
            for (int j = 0; j <= size[v] && j < i; j ++) {
                f[u][i] = max(f[u][i], f[u][i-j] + f[v][j]);
            }
        }
    }
}
int main() {
    cin >> n >> m;
    if (m > n) m = n;
    for (int i = 1; i <= n; i ++) {
        cin >> p >> s[i];
        g[p].push_back(i);
    }
    dfs(0);
    cout << f[0][m+1] << endl;
    return 0;
}
posted @ 2020-02-02 22:05  quanjun  阅读(127)  评论(0编辑  收藏  举报