zoukankan      html  css  js  c++  java
  • 《算法竞赛进阶指南》0x54树形DP 背包类树形DP Acwing 286选课

    题目链接:https://www.acwing.com/video/472/

    给定n门课,存在先修关系,构成一个森林,修一门课之前他的先修课程一定要完成,问在选m门课的情况下最多能获得多少学分?

    如果没有先修规则就是一个裸的01背包问题。但这个问题不能用01背包解决。

    而是一个分组背包问题,设f[x,j]为x为根的子树中修j门的最大学分数,从结点x出发,子树的数量就是分组的数量,背包的容量从0-j-1,每个分组中第k个物品的体积是k。

    在每个点的子树处理完之后都要加上该点的分数,如果是虚拟节点x,选中之后加上的学分是0。在算体积为i时刻的学分数时用的是i-1时刻的最大学分数,所以要倒叙保证在计算i之前i-1没有改变。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    using namespace std;
    const int maxn = 310;
    int n,m;
    int f[maxn][maxn];
    int w[maxn];
    vector<int> G[maxn];
    
    void dfs(int x){
        for(int i=0;i<G[x].size();i++){//物品 
            int y=G[x][i];
            dfs(y);
            for(int j=m-1;j>=0;j--)//体积 
                for(int k=1;k<=j;k++){//决策 
                    f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]);
                }
        }
        if(x!=0)
            for(int i=m;i;i--)
                f[x][i]=f[x][i-1]+w[x];//加上必须课程,反向跌代 
    }
    int main(){
        cin>>n>>m;
        int p;
        for(int i=1;i<=n;i++){
            cin>>p>>w[i];
            G[p].push_back(i);
        }
        m++;//加上了虚拟节点0,0也算一个 
        dfs(0);
        cout<<f[0][m]<<endl;
        
        return 0;
    }
  • 相关阅读:
    PHP发送邮件
    SQL删除字段及判断字段是否存在的方法
    密码MySQL的root的密码
    java socket 最简单的例子(server 多线程)
    php编写最简单的webservice
    SQL Server 存储过程与触发器
    手动创建最简单的JSP 文件
    Oracle 卸载步骤
    编写 WebService 程序
    eclipse 常用快捷键
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13405803.html
Copyright © 2011-2022 走看看