LibreOJ 10154 选课
题目链接:LibreOJ 10154 选课
题目大意:
题解:
树上的背包问题。
不妨给所有没有先修课程的课增加先修课程\(0\),设\(dp[i][j]\)表示以\(i\)作为根的子树修读\(j\)门课获得的最大学分。
状态转移方程:
\[dp[u][i] = max\{dp[u][i], dp[u][i - j] + dp[v][j]\}
\]
由于想要这棵子树上的学分,\(u\)为必修课程,所以还需要更新一遍\(u\)的学分。
\[dp[u][i] = dp[u][i - 1] + score[u]
\]
答案为\(dp[0][m]\)。
#include <cstring>
#include <iostream>
using namespace std;
#define N 110
int n, m, score[N], head[N], cnt, dp[N][N];
struct Edge {
int v, next;
} edge[N];
void addEdge(int u, int v) {
edge[++cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
void dfs(int u) {
dp[u][0] = 0;
for (int p = head[u]; ~p; p = edge[p].next) {
int v = edge[p].v;
dfs(v);
for (int i = m; i >= 0; --i) {
for (int j = 0; j <= i; ++j) {
dp[u][i] = max(dp[u][i], dp[u][i - j] + dp[v][j]);
}
}
}
if (u) {
for (int i = m; i >= 1; --i) {
dp[u][i] = dp[u][i - 1] + score[u];
}
}
}
int main() {
memset(head, -1, sizeof(head));
cin >> n >> m;
for (int i = 1, x; i <= n; ++i) {
cin >> x >> score[i];
addEdge(x, i);
}
dfs(0);
cout << dp[0][m];
return 0;
}