《算法竞赛进阶指南》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;
}

 

posted @ 2020-07-30 19:05  WA自动机~  阅读(214)  评论(0编辑  收藏  举报