题目描述
在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 NN 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b)。一个学生要从这些课程里选择 MM 门课程学习,问他能获得的最大学分是多少?
输入格式
第一行有两个整数 NN , MM 用空格隔开。( 1 \leq N \leq 3001≤N≤300 , 1 \leq M \leq 3001≤M≤300 )
接下来的 NN 行,第 I+1I+1 行包含两个整数 k_iki和 s_isi, k_iki 表示第I门课的直接先修课,s_isi 表示第I门课的学分。若 k_i=0ki=0 表示没有直接先修课(1 \leq {k_i} \leq N1≤ki≤N , 1 \leq {s_i} \leq 201≤si≤20)。
输出格式
只有一行,选 MM 门课程的最大得分。
输入输出样例
输入 #1复制
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
输出 #1复制
13
题解
虚拟出一个编号为0的课程为祖先,故预处理总课程数+1。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e2 + 10;
int n, m, cnt, h[maxn], s[maxn], dp[maxn][maxn];
struct edge { int to, next; } e[maxn];
inline const int read()
{
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); }
return x * f;
}
void addEdge(int u, int v)
{
e[cnt].to = v;
e[cnt].next = h[u];
h[u] = cnt++;
}
void dfs(int u)
{
for (int i = h[u]; ~i; i = e[i].next)
{
int v = e[i].to;
dfs(v);
for (int j = m - 1; j >= 0; j--)
for (int k = 0; k <= j; k++)
dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k]);
}
for (int i = m; i > 0; i--) dp[u][i] = dp[u][i - 1] + s[u];
// dp[u][0] = 0;
}
int main()
{
memset(h, -1, sizeof(h));
n = read(); m = read() + 1;
for (int i = 1; i <= n; i++)
{
addEdge(read(), i);
s[i] = read();
}
dfs(0);
printf("%d\n", dp[0][m]);
return 0;
}