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

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][jk]+f[v][k],k[0,j1]))


精♂妙的理解

【Luogu P2014 选课】【树形背包学习笔记】_第1张图片

f[i][j] 就是相当于节点 i 有个容量为 j 的背包,有 s 个儿子,物品就有 s 组,每组物品价值为 f[si][0]...f[si][] ,体积为 0...
然后就是分组背包,每组只能选一个物品,要使价值最大,也就是对于每组枚举选几件和枚举体积。


代码

#include 

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;   
}

你可能感兴趣的:(-----,DP,-----,-,树形,DP)