洛谷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;
}