洛谷P2014 TYVJ1051 选课

时间:2023-03-10 05:32:21
洛谷P2014   TYVJ1051 选课

题目描述

在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有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倒序循环(分组背包思想)

代码如下:

 /*by SilverN*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
const int mxn=;
int n,m;
vector<int>e[mxn];
int sc[mxn];
int f[mxn][mxn];//[课程序号][本层选课数]
void add_edge(int u,int v){
e[v].push_back(u);
return;
}
void dp(int x){
for(int i=;i<=m;i++)f[x][i]=sc[x];
for(int i=;i<e[x].size();i++){
int v=e[x][i];
dp(v);
for(int j=m;j;j--){//选j门课
for(int k=;k<j;k++){
f[x][j]=max(f[x][j],f[x][j-k]+f[v][k]);
}
}
}
return;
}
int main(){
scanf("%d%d",&n,&m);
int i,j;
int s,k;
for(i=;i<=n;i++){
scanf("%d%d",&k,&sc[i]);
add_edge(i,k);
}
m++;//虚拟根节点占用一个位置
dp();
printf("%d\n",f[][m]);
return ;
}