您的位置:首页 > 其它

【Luogu P2014 选课】【树形背包学习笔记】

2018-01-12 10:30 281 查看
Codevs1378选课[树形DP|两种做法(多叉转二叉|树形DP+分组背包)] —— By Candy?

因为依赖关系是以森林的形式给出的,增加一个虚拟节点 0 为所有无先修课节点的根。

f[i][j] 表示以 i 为根的子树中选 j 个(对应到原树中即是在节点 i 的儿子和兄弟中选 j 个)的最大价值,初始化 −inf,f[i][0]=0(不选可行),f[i][1]=wi(选一个必须选根)。

f[u][j]=max(f[u][j],max(f[u][j−k]+f[v][k],k∈[0,j−1]))

精♂妙的理解



f[i][j] 就是相当于节点 i 有个容量为 j 的背包,有 s 个儿子,物品就有 s 组,每组物品价值为 f[si][0]...f[si][节点数],体积为 0...节点数。

然后就是分组背包,每组只能选一个物品,要使价值最大,也就是对于每组枚举选几件和枚举体积。

代码

#include <bits/stdc++.h>

using namespace std;
const int N = 305;

struct Edge {
int to, next;
}e[N << 1];

int n, m;
int w
, head
;
int cnt = 0;
void add(int u, int v) {
e[++ cnt].to = v; e[cnt].next = head[u]; head[u] = cnt;
}

int son
;
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

;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: