ch_5402 选课

树上分组背包模型,

即每个子树选一种状态,状态为f[x][y]表示以x为根的子树中选y个的最大价值,怎么选并没有影响

注意y>=1则x必须选,即x必须先选了,y即其子节点才能选

同样是森林,需要建立虚点N+1连向无先行课的课

#include<iostream> 
#include<cstdio>

#define ri register int
#define u int

namespace opt {
    
    inline u in() {
        u x(0),f(1);
        char s(getchar());
        while(s<'0'||s>'9') {
            if(s=='-') f=-1;
            s=getchar();
        }
        while(s>='0'&&s<='9') {
            x=(x<<1)+(x<<3)+s-'0';
            s=getchar();
        }
        return x*f;
    }
    
}

using opt::in;

#define NN 305

namespace mainstay {
    
    u N,M,cnt,h[NN],v[NN],id[NN],f[NN][NN];
    
    struct node{
        u to,next;
    }a[NN];
    
    inline void add(const u &x,const u &y){
        a[++cnt].to=y,a[cnt].next=h[x],h[x]=cnt;
    }  
    
    void dfs(const u &x){
        for(ri i(h[x]);i;i=a[i].next){
            u _y(a[i].to);
            dfs(_y);
            for(ri j(M);j>=1;--j){
                for(ri k(0);k<=j;++k){//不理解蓝书上的话(说是要倒序才正确),这样正序枚举可以过
                    f[x][j]=std::max(f[x][j],f[x][j-k]+f[_y][k]);
                }
            }
        }
        if(x^(N+1)){
            for(ri i(M);i>=1;--i){
                f[x][i]=f[x][i-1]+v[x];
            }
        }
    }
    
    inline void solve(){
        N=in(),M=in();
        for(ri i(1);i<=N;++i){
            u _a(in()),_b(in());
            v[i]=_b;
            if(_a) add(_a,i);
            else add(N+1,i);
        }
        dfs(N+1);
        std::cout<<f[N+1][M];
    }
    
}

int main() {
    
    //freopen("x.txt","r",stdin);
    std::ios::sync_with_stdio(false);
    mainstay::solve();
    
}

 

posted @ 2019-11-07 17:07  pai_hoo  阅读(117)  评论(0编辑  收藏  举报