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
",f[0][i]);
	printf("%lld
",f[0][m][e[0]]);
}
原文地址:https://www.cnblogs.com/zhanglichen/p/14605813.html