题目传送门

一、树形DP+分组背包

\(DP\)模型:树形套分组

\(u\)为根节点,把\(u\)的每一个后继看成是一个个独立的物品组,以体积作为决策去枚举。

#include <bits/stdc++.h>

using namespace std;

const int N = 110;
int h[N], e[N], ne[N], idx;
int f[N][N];             //f[u][j] 以u为根结点的子树中,总体积不超过j的方案下,最大总价值
int v[N], w[N];
int n, m;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

//遍历以u为根结点的子树
void dfs(int u) {
    //1、如果选择了以u为根结点的子树,那么必须保证剩余空间大于等于v[u]
    //2、在能装下的情况下,而且u 结点必选,最大价值初始化为w[u]
    for (int i = v[u]; i <= m; i++) f[u][i] = w[u];
    //遍历每个子结点
    for (int i = h[u]; ~i; i = ne[i]) {
        dfs(e[i]);//通过递归子结点,准备好子结点的数据,可以依赖于子结点的数据进行判断
        for (int j = m; j >= v[u]; j--)         //u和子树一共的体积
            for (int k = 0; k <= j - v[u]; k++) //分给子树e[i]的体积
                f[u][j] = max(f[u][j], f[u][j - k] + f[e[i]][k]);
    }
}

int main() {
    cin >> n >> m;
    memset(h, -1, sizeof(h));
    int root;
    for (int i = 1; i <= n; i++) {
        int p;
        cin >> v[i] >> w[i] >> p;
        if (p == -1) root = i;
        else add(p, i);
    }
    //树上dp
    dfs(root);
    printf("%d\n", f[root][m]);//整棵树,在体积为m时的最大价值
    return 0;
}


二、多叉树转二叉树实现

多叉树转二叉树

三、dfs序

TODO 此方法还没有研究明白,不准备讲

#include <bits/stdc++.h>

using namespace std;
const int N = 110;
/*
需要先学会dfs序,dfs序后倒着做01背包,f[i][j]可以理解为从i往后剩余j的容量产生的最大值
*/

//邻接表
int h[N], ne[N], e[N], idx;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int n, m;
int v1[N], w1[N];
int f[N][N];
//此题我目前dfs序的功力不够,需要学习dfs序后再来进行思考解决
//TODO
int size[2 * N];        //以i为根结点的子树中结点的个数
int dfs_order[2 * N];
int v2[N], w2[N];
int pos;

/**
 * 功能:dfs序
 * 本质:就是执行一遍树的dfs,找一个辅助数组记录一下dfs序
 * @param p 结点编号
 */
void dfs(int p) {
    dfs_order[p] = ++pos; //记录结点p在dfs序中是第几个,下标从1开始

    //维护一下子树的结点个数
    size[pos] = 1;        //以dfs序中pos号为根的子树中结点个数
    v2[pos] = v1[p];      //将原来树中p号结点的体积转移到pos号dfs序的结点体积上去
    w2[pos] = w1[p];      //将原来树中p号结点的价值转移到pos号dfs序的结点价值上去

    //遍历邻接表
    for (int i = h[p]; i != -1; i = ne[i]) {
        int j = e[i];
        dfs(j);
        //收集所有子结点的个数
        size[dfs_order[p]] += size[dfs_order[j]];
    }
}

int main() {
    cin >> n >> m;
    int root;
    //初始化邻接表
    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i++) {
        int p;
        cin >> v1[i] >> w1[i] >> p;
        if (p == -1) root = i;  //记录根结点
        else add(p, i);     //维护邻接表
    }

    //dfs序
    dfs(root);

    //倒着做01背包
    for (int i = n; i >= 1; i--)
        for (int j = 0; j <= m; j++) {
            f[i][j] = f[i + size[i]][j];
            if (j >= v2[i]) f[i][j] = max(f[i][j], f[i + 1][j - v2[i]] + w2[i]);
        }

    //输出
    cout << f[1][m] << endl;
    return 0;
}

相关文章: