洛谷 选课 【例题精讲】

题目描述

 

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

 

输入输出格式

输入格式:

 

第一行有两个整数N,M用空格隔开。(1<=N<=300,1<=M<=300)

 

接下来的N行,第I+1行包含两个整数ki和si, ki表示第I门课的直接先修课,si表示第I门课的学分。若ki=0表示没有直接先修课(1<=ki<=N, 1<=si<=20)。

 

输出格式:

 

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

 
 
这是我见过的最纯萃最标准的树形dp了,难度不是很大,我们简单说说就好,一看代码一切都解决了;
有一个比较神奇的地方就是,输入进来的是一个森林,不确定根节点,但是没有父节点的点输入的为零,这就启发我们,可以虚构一个并不存在的0科目,是所有根节点的共同根节点,价值也为零;
设f[i][j]代表以i为根的子树中,选j门课程所获得的最大价值,那么我们只需要通过递归,从深到浅解决问题即可!
但是我们最终的结果如果是f[0][m]的话,我们需要在动态规划之前把m加一,因为我们要选的课,除了我们的那个限制,还得选第0门课,而这门课不占用我们的课数(也不占用价值),所以说m++;
下面上代码!
 
 
 1 //Is mule is horse please pull out and six six.--Luo
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<string>
 8 #include<algorithm>
 9 #include<vector> 
10 using namespace std;
11 const int MAXN=405;
12 int z,x,c,v,b,n,m,s;
13 int f[MAXN][MAXN]={0};//f[i][j]表示选择第i门先修课,连他在内选择j门子课程所赚的最大学分; 
14 vector<int>xxk[MAXN];//xianxiuke选修课;
15 int cre[MAXN]={0};
16 void dp(int x)
17 {
18     for(int i=1;i<=m;i++) f[x][i]=cre[x];
19     for(int i=0;i<xxk[x].size();i++)
20     {
21         int v=xxk[x][i];
22         dp(v);
23         for(int j=m;j>=1;j--)
24         {
25             for(int k=0;k<j;k++)
26             {
27                 f[x][j]=max(f[x][j],f[x][j-k]+f[v][k]);
28 //选第x门课j门子课程或学分的最大值由x门课j-k门子课程所获最大学分加选x的某门子课程和(包括在内)x的某门子课程的k门子课程; 
29             }
30         }
31     }
32     return ;
33 }
34 int main()
35 {
36     scanf("%d%d",&n,&m);
37     for(int i=1;i<=n;i++)
38     {
39         scanf("%d%d",&s,&cre[i]);
40         xxk[s].push_back(i);
41     }
42     m++;
43     dp(0);
44     cout<<f[0][m]<<endl;
45     return 0;
46 }
View Code

 

 
posted @ 2018-04-05 16:58  杜宇一声  阅读(438)  评论(0编辑  收藏  举报