洛谷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;
}
原文地址:https://www.cnblogs.com/quanjun/p/12253966.html