1. 1. 题目
  2. 2. [CTSC1997] 选课
    1. 2.1. 题目描述
  3. 3. 题解
  4. 4. 问题
  5. 5. 代码

题目

[CTSC1997] 选课

题目描述

在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 NN 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程 a 是课程 b 的先修课即只有学完了课程 a,才能学习课程 b)。一个学生要从这些课程里选择 MM 门课程学习,问他能获得的最大学分是多少?

1N3001 \leq N \leq 300 , 1M3001 \leq M \leq 300

题解

树上dp+背包

f[u][i][j]表示以u为根的子树中,前i颗u的子树,选j颗的 获得的最大学分

然后发现和01背包差不多,只不过需要枚举一下u中的每一颗子树选多少(j为多少)。

可以滚动数组+倒叙循环把空间去一维

问题

子树选的数目不能等于q,否则这颗子树的根节点就选不了了,导致后代所有的课都选不了(行48)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<bits/stdc++.h>

using namespace std;

int read()
{
int f = 1, x = 0;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') f = -1;
ch = getchar();
}

while(ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return f * x;
}

const int N = 310;

int n, m;

int s[N], size[N];

int head[N], nxt[N], to[N], cnt = 1;

int f[N][N];//表示以u为子树中选m个学科最多能获得多少学分

void add(int x, int y)
{
nxt[++cnt] = head[x];
to[cnt] = y;
head[x] = cnt;
}

void dfs(int u)
{
for(int i = head[u]; i; i = nxt[i])
{
int v = to[i];
dfs(v);
for(int q = m;q >= 0;q--)
{
for(int k = 0;k < q/*子树选的数目不能等于q,否则这颗子树的根节点就选不了了,导致后代所有的课都选不了*/;k++)
{
f[u][q] = max(f[u][q], f[v][k] + f[u][q - k]);
}
}
}
}

int main()
{
n = read(), m = read() + 1;

int anc;

for(int i = 1;i <= n;i++)
{
anc = read();
s[i] = read();
add(anc, i);
f[i][1] = s[i];
}
dfs(0);
cout << f[0][m];
}