题目描述
在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b)。一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少?
输入输出格式
输入格式:
第一行有两个整数N,M用空格隔开。(1<=N<=200,1<=M<=150)
接下来的N行,第I+1行包含两个整数ki和si, ki表示第I门课的直接先修课,si表示第I门课的学分。若ki=0表示没有直接先修课(1<=ki<=N, 1<=si<=20)。
输出格式:
只有一行,选M门课程的最大得分。
输入输出样例
输入样例#1:
7 4 2 2 0 1 0 4 2 1 7 1 7 6 2 2
输出样例#1:
13
树形DP 分组背包思想
f[i][j]表示第i门课程,包括其自身在内选j门子课程的最大收益
另加一维k表示子课程选了多少门,可得出f[i][j]=max(f[i][j], f[i][j-k]+f[(i的子结点v)][k] k∈[0,j) (不能取k=j,因为至少有一门是先修课v)
j倒序循环(分组背包思想)
代码如下:
1 /*by SilverN*/ 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cstdio> 6 #include<cmath> 7 #include<vector> 8 using namespace std; 9 const int mxn=410; 10 int n,m; 11 vector<int>e[mxn]; 12 int sc[mxn]; 13 int f[mxn][mxn];//[课程序号][本层选课数] 14 void add_edge(int u,int v){ 15 e[v].push_back(u); 16 return; 17 } 18 void dp(int x){ 19 for(int i=1;i<=m;i++)f[x][i]=sc[x]; 20 for(int i=0;i<e[x].size();i++){ 21 int v=e[x][i]; 22 dp(v); 23 for(int j=m;j;j--){//选j门课 24 for(int k=0;k<j;k++){ 25 f[x][j]=max(f[x][j],f[x][j-k]+f[v][k]); 26 } 27 } 28 } 29 return; 30 } 31 int main(){ 32 scanf("%d%d",&n,&m); 33 int i,j; 34 int s,k; 35 for(i=1;i<=n;i++){ 36 scanf("%d%d",&k,&sc[i]); 37 add_edge(i,k); 38 } 39 m++;//虚拟根节点占用一个位置 40 dp(0); 41 printf("%d ",f[0][m]); 42 return 0; 43 }