• AcWing 10. 有依赖的背包问题


    题目传送门

    一、树形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;
    }
    
    
  • 相关阅读:
    工作常用mysql命令以及函数
    mybati 字段映射
    关于idea切换账号,上传的代码依旧是之前账号提交/操作git
    java 开发过程中常用
    简单了解微服务
    zookeeper 学习(二) java操作zookeeper
    zookeeper 学习(一) 初识zookeeper
    漫画:我们为何结婚,又为何不忠?
    适用 selenium 自动化的十大测试场景
    女朋友买房了,我我我....
  • 原文地址:https://www.cnblogs.com/littlehb/p/15719622.html
Copyright © 2020-2023  润新知