Codevs1378选课[树形DP|两种做法(多叉转二叉|树形DP+分组背包)] —— By Candy?
因为依赖关系是以森林的形式给出的,增加一个虚拟节点 0 为所有无先修课节点的根。
f[i][j]
表示以
i
为根的子树中选
f[u][j]=max(f[u][j],max(f[u][j−k]+f[v][k],k∈[0,j−1]))
精♂妙的理解
f[i][j]
就是相当于节点
i
有个容量为
然后就是分组背包,每组只能选一个物品,要使价值最大,也就是对于每组枚举选几件和枚举体积。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 305;
struct Edge {
int to, next;
}e[N << 1];
int n, m;
int w[N], head[N];
int cnt = 0;
void add(int u, int v) {
e[++ cnt].to = v; e[cnt].next = head[u]; head[u] = cnt;
}
int son[N];
void dfsson(int u) {
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
dfsson(v);
son[u] += son[v];
son[u] = min(son[u], m);
}
}
int f[N][N];
void dfs(int u) {
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
dfs(v);
for (int j = son[u]; j >= 0; j --) // 背包容量
for (int k = 0; k <= j - 1; k ++) // item of group
f[u][j] = max(f[u][j], f[u][j - k] + f[v][k]);
}
}
int main() {
memset(son, 0, sizeof(son));
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++) {
int fa;
scanf("%d%d", &fa, &w[i]);
add(fa, i); son[fa] ++;
}
memset(f, -1, sizeof(f));
for (int i = 0; i <= n; i ++)
f[i][0] = 0, f[i][1] = w[i];
dfsson(0);
for (int i = 0; i <= n; i ++) son[i] ++; // 因为新加了虚拟根节点 0,所以要多选一门学分为 0 的课程节点 0
dfs(0);
printf("%d\n", f[0][son[0]]);
return 0;
}