C# · 12月 20, 2021

C++ P2014 选课

题目描述

在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有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门课程的最大得分。

输入输出样例

输入样例#1: 

7 4

2 2

0 1

0 4

2 1

7 1

7 6

2 2

输出样例#1: 

13

题目地址:https://www.luogu.org/problemnew/show/P2014

个人思路:

本质上仍然是背包问题,但是这里把背包问题转化到了树上

所以,在dfs的同时进行背包dp(分组背包)即可

#include

#include

using namespace std;

int score[1010],ans,n,m,f[1010][1010],head[1010],cnt;

struct edge{

int from,to,nxt;

}e[1010];

void add(int from,int to){

e[++cnt].from=from;

e[cnt].to=to;

e[cnt].nxt=head[from];

head[from]=cnt;

}

void dp(int x){

for(int i=head[x];i;i=e[i].nxt){

int v=e[i].to;

dp(v);

for(int t=m;t;t–)//循环当前选课总门数(背包体积)

for(int j=t;j;j–){//循环更深子树上的选课门数(组内物品)

if(t-j>=0)

f[x][t]=max(f[x][t],f[x][t-j]+f[v][j]);

ans=max(ans,f[x][t]);

}

}

if(x!=0)

for(int t=m;t;t–){

f[x][t]=f[x][t-1]+score[x];

ans=max(ans,f[x][t]);

}

}

int main(){

ans=-2000000000;

cin>>n>>m;

int fa;

for(int i=1;i<=n;i++){

cin>>fa>>score[i];

add(fa,i);

}

dp(0);

printf(“%dn”,ans);

return 0;

}