一.题目链接:
选课
二.题目大意:
有 n 门课,从中选出 m 门课,每门课有各自的学分,当选择课程 i 时,课程 i 的前置课程也必须选(至多一个).
求最大学分.
三.分析:
由于课程 i 至多只有一个前置课程,因此呈森林结构,设置 0 好号节点后,便成为了 n + 1 的点 n 条边的树.
设 dp[u][j] 表示以节点 u 为根的子树中,选择 j 门课程学分的最大值,c[k] 表示 u 的第 k 棵子树中选择了 c[k] 门课.
则有状态转移方程:
即对于节点 u 来说,他有 |son(u)| 组物品可以选,第 i 组的第 j 个物品体积为 j
扯不下去了,详见代码吧.
四.代码实现:
#include <bits/stdc++.h>
using namespace std;
const int M = (int)3e2;
int n, m;
int cnt;
int head[M + 5];
struct node
{
int v, nx;
} Edge[M + 5];
int score[M + 5];
int dp[M + 5][M + 5];
void init()
{
cnt = 0;
for(int i = 0; i <= n; ++i)
{
head[i] = -1;
}
}
void add(int u, int v)
{
Edge[cnt].v = v;
Edge[cnt].nx = head[u];
head[u] = cnt++;
}
void dfs(int u)
{
dp[u][0] = 0;
for(int i = head[u]; ~i; i = Edge[i].nx)
{
int v = Edge[i].v;
dfs(v);
for(int j = m; j >= 0; --j)
{
for(int k = 0; k <= j; ++k)
dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k]);
}
}
if(u)
{
for(int i = m; i >= 1; --i)
dp[u][i] = dp[u][i - 1] + score[u];
}
}
int main()
{
// freopen("input.txt", "r", stdin);
scanf("%d %d", &n, &m);
init();
for(int i = 1, fa; i <= n; ++i)
{
scanf("%d %d", &fa, &score[i]);
add(fa, i);
}
dfs(0);
printf("%d\n", dp[0][m]);
return 0;
}