Nowcoder51179.选课(树形背包)
学校实行学分制。
每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。
学校开设了 N 门的选修课程,每个学生可选课程的数量 M 是给定的。
学生选修了这 M 门课并考核通过就能获得相应的学分。
在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其他的一些课程的基础上才能选修。
例如《Windows程序设计》必须在选修了《Windows操作基础》之后才能选修。
我们称《Windows操作基础》是《Windows程序设计》的先修课。
每门课的直接先修课最多只有一门。
两门课可能存在相同的先修课。
你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修条件。
假定课程之间不存在时间上的冲突。
注意滚动数组转移时的细节。
//f(i,j)表示在节点i的子树内选择j个节点所能得到的最大学分
#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
typedef long long ll;
ll f[maxn][maxn][2];
int e[maxn];
int a[maxn];
vector<int> g[maxn];
int n,m;
int rt=0;
int size[maxn];
void dfs (int x) {
size[x]=1;
f[x][1][1]=a[x];
int k=0;
for (int y:g[x]) {
dfs(y);
for (int i=0;i<=n;i++) f[x][i][k]=0;
for (int i=min(m,size[x]);i>=1;i--) {
for (int j=min(m,size[y]);j>=0;j--) {
if (i+j<=m)
f[x][i+j][k]=max(f[x][i+j][k],f[x][i][k^1]+f[y][j][e[y]]);
}
}
size[x]+=size[y];
k^=1;
}
e[x]=(k^1);
}
int main () {
scanf("%d%d",&n,&m);
m++;
for (int i=1;i<=n;i++) {
int x;
scanf("%d%d",&x,&a[i]);
g[x].push_back(i);
}
dfs(0);
//for (int i=1;i<=m;i++) printf("%d\n",f[0][i]);
printf("%lld\n",f[0][m][e[0]]);
}