[CTSC1997]选课

题目描述

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

输入格式

第一行有两个整数 NN , MM 用空格隔开。( 1 leq N leq 3001N300 , 1 leq M leq 3001M300 )

接下来的 NN 行,第 I+1I+1 行包含两个整数 k_iki和 s_isik_iki 表示第I门课的直接先修课,s_isi 表示第I门课的学分。若 k_i=0ki=0 表示没有直接先修课(1 leq {k_i} leq N1kiN , 1 leq {s_i} leq 201si20)。

输出格式

只有一行,选 MM 门课程的最大得分。

输入输出样例

输入 
7  4
2  2
0  1
0  4
2  1
7  1
7  6
2  2
输出
13

sol:f[i][j]表示从i这棵子树中选j门课的最大学分,所以有f[i][k] = max(f[i][k], f[i][k - j] + f[x][j]) (1 ≤ k ≤ m , 0 ≤ j < k)
因为有可能有多棵树,所以把每棵树的根节点连向0,选课的时候从0开始选,自然选的课程数m也要加1
注意k要倒序枚举因为由于 f[i][k] 由 f[i][k - j] 更新得到,那么就需要保证更新 f[i][k] 的时候 f[i][k - j] 没有被更新,由于 j 是一个正整数,所以我们倒序枚举 k 即可
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,head[200002],tot,root,f[1001][1001];
 4 struct data {
 5     int to,nxt;
 6 } e[200002];
 7 void add(int x,int y) {
 8     e[++tot].to=y;
 9     e[tot].nxt=head[x];
10     head[x]=tot;
11 }
12 void dp(int x) {
13     for(int i=head[x]; i; i=e[i].nxt) {
14         int v=e[i].to;
15         dp(v);
16         for(int k=m+1; k>=1; k--)
17             for(int j=0; j<k; j++)
18                 f[x][k]=max(f[v][j]+f[x][k-j],f[x][k]);
19     }
20 }
21 int main() {
22     scanf("%d%d",&n,&m);
23     for(int i=1; i<=n; i++) {
24         int x,y;
25         scanf("%d%d",&x,&y);
26         add(x,i);
27         f[i][1]=y;
28     }
29     dp(0);
30     printf("%d
",f[0][m+1]);
31     return 0;
32 }
原文地址:https://www.cnblogs.com/sbwll/p/13391223.html