[CTSC1997] 选课 树上背包
[CTSC1997] 选课 树上背包
题意
选择一些课程,每个课程有一个唯一的先修课程,每个课程有一个学分,今有一名学生需要从中选择\(m\)门课程,问能够获得的最大学分是多少,每门课程的选择前提是选择先修课程
\[1\leq n\leq 300\\
1\leq m\leq 300
\\
k_i和s_i表示第i门课程的先修课程,s_i表示第i门课的学分
\]
分析
每个点有唯一的前驱,因此可以转化为树上问题
用\(dp[i][j]\)表示第\(i\)个结点选择了\(j\)个子孙结点能够取到的最大学分,这就把问题转化成了树上背包问题
\[dp[i][j] = max(dp[i][j - k + 1] + dp[v][k])
\]
根据这个方程,在树上DFS一遍就可以了
注意细节,因为第二维不算自己,所以要注意+1-1
当然这里还有细节,刷新的时候要从大到小枚举,否则会影响后面的答案
代码
vector<int> e[505];
int dp[505][505];
int w[505];
int dfs(int u) {
if (!e[u].size()) return 0;
int siz = 0;
for (auto v : e[u]) {
int t = dfs(v);
siz += t + 1;
for (int j = siz; j >= 0; j--)
for (int k = 0; k <= min(t,j - 1); k++)
dp[u][j] = max(dp[u][j], dp[u][j - k - 1] + dp[v][k]);
}
return siz;
}
int main() {
int n = readint();
int m = readint();
for (int i = 1; i <= n; i++) {
int x = readint();
int y = readint();
if (!x) e[0].push_back(i);
else e[x].push_back(i);
w[i] = y;
}
for (int i = 1; i <= n; i++)
dp[i][0] = w[i];
dfs(0);
cout << dp[0][m];
}